javascript狂想曲(一)

近期会对js世界里面的知识进行归纳总结,为以后阅读源码打好基础。

1 Object.assign

Object.assign() 方法用于将所有可枚举的属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

Object.assign(target, ...sources)

var o1 = { a: 1 };
var o2 = { b: 2,c:{a:5,b:6} };
var o3 = { c: {d:7} };

var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: {d:7} }
console.log(o1);  // { a: 1, b: 2, c: {d:7} }
obj.b = 5
console.log(o2)  // { b: 2,c:{a:5,b:6} }

这里看出object.assign是浅克隆,无法将里面一层对象拷贝。

如何实现深拷贝

function deepClone(data){
      var type = Object.prototype.toString.call(data).slice(-8),
      o;

      if( type == 'Array'){
           o = [];
      }else if(type == 'Object'){
           o = {}
      }else{
           return data
      }

      if( type == 'Array'){
           for(var i = 0; i< data.length;i++){
                   o.push(deepClone(data[i]))
          }
      }else{
           for(var i in data){
                o[i] = deepClone(data[i])
          }
      }
      return o
}

这样来说明深浅克隆的区别吧:

var data = { a:6,b: 2,c:{a:5,b:6} }
var aa = deepClone(data)
aa.c = 6
data  //  { a:6,b: 2,c:6}

对比之前可以看出来了吧,深克隆改变自身会对原目标造成影响,而浅克隆不会。

继承属性和不可枚举属性是不能拷贝的
var obj = Object.create({foo: 1}, { // foo 是个继承属性。
    bar: {
        value: 2  // bar 是个不可枚举属性。
    },
    baz: {
        value: 3,
        enumerable: true  // baz 是个自身可枚举属性。
    }
});

var copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }

我们来见写一个属性融合吧。

function create(target,source){
    for(var i in source){
         if(source.hasOwnProperty(i)){
            target.prototype[i] = source[i]
        }
   }

    function aa(){}
    aa.prototype = target.prototype
    return new aa()
}

function target(){} qwe.prototype.say = function(){}
var source = {dance:function(){console.log('sss')},
dan:function(){console.log('dddd')}
}
var instance = create(target,source)
instance

相信不用我解释大家也看得很明白了。

2 Object.create

Object.create() 方法会使用指定的原型对象及其属性去创建一个新的对象。

Object.create(proto, [ propertiesObject ])

//Shape - superclass
function Shape() {
  this.x = 0;
  this.y = 0;
}

Shape.prototype.move = function(x, y) {
    this.x += x;
    this.y += y;
    console.info("Shape moved.");
};

// Rectangle - subclass
function Rectangle() {
  Shape.call(this); //call super constructor.
}

// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

var rect = new Rectangle();

console.log('Is rect an instance of Rectangle?',
  rect instanceof Rectangle); // true
console.log('Is rect an instance of Shape?',
  rect instanceof Shape); // true

rect.move(1, 1); //Outputs, "Shape moved."

我们知道在js原型继承里面这样可以实现继承

function parent(){}
function child(){}
child.prototype = new parent()

这其实正好对应下这段代码

Rectangle.prototype = Object.create(Shape.prototype);
// Object.create  返回就是shape得实例

我们用shape.call(this)其实就是把那段构造也写上去。

function aa(){}   aa.prototype = {ww:function(){console.log('fff')}}

var o = Object.create(aa.prototype, {
  // foo会成为所创建对象的数据属性
  foo: { 
    writable:true,
    configurable:true,
    value: "hello" 
  },
  // bar会成为所创建对象的访问器属性
  bar: {
    configurable: false,
    get: function() { return 10 },
    set: function(value) {
      console.log("Setting `o.bar` to", value);
    }
  }
});
o.ww()   // ffff
o.foo  // hello
o.bar // 10
o.bar = 6 // Setting o.bar to, 10

想必大家已经看明白了,如果我们想要把普通人和程序员结合,我们可以这样做:

function person(){}  
person.prototype = {
     walk:function(){},
     eat:function(){}
}

//程序员必备技能
var skills = {
      coding:{
          writable:true,
          configurable:true,
          value: function(){console.log('i am coding')}
      },
     debug:{} ....
}

// 创造一个结合体
var mix = Object.create(person.prototype,skills)
mix.coding() // i am coding

需要注意的是,value为函数值得写法,我们现在创造了一个不平凡的人。

3 arguments

arguments对象不是一个 Array 。它类似于数组,但除了 长度之外没有任何数组属性。例如,它没有 pop 方法。但是它可以被转换为一个真正的数组:

let args = Array.prototype.slice.call(arguments);
let args = [].slice.call(arguments);

我们需要把三个人用'---' 拼接起来

function myConcat(separator) {
  var args = Array.prototype.slice.call(arguments, 1);
  return args.join(separator);
}

myConcat("---", "red", "orange", "blue");
// red --- orange --- blue

同样我们可以用这种方式创建一个html元素

function list(type) {
  var result = "<" + type + "l><li>";
  var args = Array.prototype.slice.call(arguments, 1);
  result += args.join("</li><li>");
  result += "</li></" + type + "l>"; // end list

  return result;
}

var listHTML = list("ul", "One", "Two", "Three");
"<ul><li>One</li><li>Two</li><li>Three</li></ul>"

今天是情人节,老婆大人让我对他连续说 i love you ,直到。。。好吧,就说一分钟的我爱你吧。。

function sayLove(){
     var time = +new Date()
     return function(){
           console.log('I love u')
           if(+new Date() - time < 60*1000){
                 arguments.callee()
          }
     }
}

我刚刚做了一个测试,程序每秒钟可以说700次 i love u。。

var global = this;

var sillyFunction = function (recursed) {
    if (!recursed) { return arguments.callee(true); }
    if (this !== global) {
        alert("This is: " + this);
    } else {
        alert("This is the global");
    }
}

sillyFunction(); // object arguments

严格模式下,第5版 ECMAScript (ES5) 禁止使用 arguments.callee()。当一个函数必须调用自身的时候, 避免使用 **arguments.callee(), **通过要么
给函数表达式一个名字,要么使用一个函数声明.

尾递归调用

// 尾调用
function f(x){
  return g(x);
}
// 非尾调用
function f(x){
  return g(x) + 1;
}

尾调用的概念非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。
函数调用自身,称为递归。如果尾调用自身,就称为尾递归。

function factorial(n) {
  if (n === 1) return 1;
  return n * factorial(n - 1);  // 非尾递归
}

factorial(50000) // 栈溢出

如果改写成尾递归,只保留一个调用记录,复杂度 O(1) 。

function factorial(n, total) {
  if (n === 1) return total;
  return factorial(n - 1, n * total);
}

factorial(5, 1) // 120
史上最简单的的数组去重
---- 原始做法----
function make(arr){
     let aa = arr.filter( (item, index) =>
           index == arr.indexOf(arr)
  )
 return aa
}
make([1,2,3,,2,3,1,5]) // 1,2,3,5

升级做法

et arr = [1,1,2,,2,3,3,4,5,5], set = new Set(arr), aa = Array.from(set)
aa // [1,2,3,4,5]

4 promise

本文针对有promise基础的人,基本知识这里不讲解。

function myAsyncFunction(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.onload = () => resolve(xhr.responseText);
    xhr.onerror = () => reject(xhr.statusText);
    xhr.send();
  });
};

我们上面定义了一个promise,代执行玩ajax后调用函数。

myAsyncFunction(url).then(value => console.log(value),
value => console.log(value))

前面一个是成功回调,后面一个是失败回调。

Promise.prototype.then方法返回的是一个新的Promise对象,因此可以采用链式写法。

myAsyncFunction("/posts.json").then(function(json) {
  return json.post;
}).then(function(post) {
    console.log(post)  //这里打印的事json.post
});

需求来了,听好了:现在我需要通过ajax去后台哪一个数据,数据里面有个url,在请求成功后我还需要再去请求这个url的地址,进行最后一步操作,怎么做?

myAsyncFunction("/posts.json").then(function(json) {
  return myAsyncFunction(son.url);
}).then(function(post) {
    console.log(post)  //这里打印的事json.post
});

是不是很爽。。。

最后看一下promise.all的用法

var p1 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 1000, 'one'); 
}); 
var p2 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 2000, 'two'); 
});
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 3000, 'three');
});
var p4 = new Promise((resolve, reject) => {
  setTimeout(resolve, 4000, 'four');
});

Promise.all([p1, p2, p3, p4]).then(values => { 
  console.log(values);   //['one' , 'two' , 'three' , 'four']
}, reason => {
  console.log(reason)
});

我们这里一共有4个promise对象,都是异步执行,过完4s打印如上内容,其实就是每个promise返回值的数组。

其实有了这么多的知识足以在项目中运用自如了。我们来个vue的例子吧

const store = new Vuex.Store({
  actions: {
    deleteItem: ({ commit }, payload) => {
      return callPromiseAPI(payload).then(res => {
         commit('delete', { res })
      })
    },
    getList: ({ commit }, payload) => {
      return callPromiseAPI(payload).then(res => {
         commit('list', { res })
      })
    }
  }
})

function  callPromiseAPI(payload){
      return new Promise(function(resolve,reject){
              resolve(payload)
   })
}

我们需要删除一个id为1的数据然后再去查一遍数据。

store.dispatch('deleteItem', { id: 1 }).then(() => {
  // action done
  store.dispatch(getList, {page: 1})
})

我们看到了deleteItem之后,会去在查询page为1的数据,之前的deleteItem返回的也是promise可以继续.then。
好了,今天就到这里了吧。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容