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
javascript狂想曲(一)_第1张图片

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

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>
  • "; var args = Array.prototype.slice.call(arguments, 1); result += args.join("
  • "); result += "
  • "; // end list return result; } var listHTML = list("ul", "One", "Two", "Three"); "
    • One
    • Two
    • Three
    "

    今天是情人节,老婆大人让我对他连续说 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。
    好了,今天就到这里了吧。。。

    你可能感兴趣的:(javascript狂想曲(一))