javascript狂想曲(一)

近期會對js世界里面的知識進行歸納總結(jié),為以后閱讀源碼打好基礎(chǔ)。

1 Object.assign

Object.assign() 方法用于將所有可枚舉的屬性的值從一個或多個源對象復(fù)制到目標對象。它將返回目標對象。

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是淺克隆,無法將里面一層對象拷貝。

如何實現(xiàn)深拷貝

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
}

這樣來說明深淺克隆的區(qū)別吧:

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() 方法會使用指定的原型對象及其屬性去創(chuàng)建一個新的對象。

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原型繼承里面這樣可以實現(xiàn)繼承

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

這其實正好對應(yīng)下這段代碼

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

我們用shape.call(this)其實就是把那段構(gòu)造也寫上去。

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

var o = Object.create(aa.prototype, {
  // foo會成為所創(chuàng)建對象的數(shù)據(jù)屬性
  foo: { 
    writable:true,
    configurable:true,
    value: "hello" 
  },
  // bar會成為所創(chuàng)建對象的訪問器屬性
  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

想必大家已經(jīng)看明白了,如果我們想要把普通人和程序員結(jié)合,我們可以這樣做:

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

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

// 創(chuàng)造一個結(jié)合體
var mix = Object.create(person.prototype,skills)
mix.coding() // i am coding

需要注意的是,value為函數(shù)值得寫法,我們現(xiàn)在創(chuàng)造了一個不平凡的人。

3 arguments

arguments對象不是一個 Array 。它類似于數(shù)組,但除了 長度之外沒有任何數(shù)組屬性。例如,它沒有 pop 方法。但是它可以被轉(zhuǎn)換為一個真正的數(shù)組:

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

同樣我們可以用這種方式創(chuàng)建一個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>"

今天是情人節(jié),老婆大人讓我對他連續(xù)說 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()。當一個函數(shù)必須調(diào)用自身的時候, 避免使用 **arguments.callee(), **通過要么
給函數(shù)表達式一個名字,要么使用一個函數(shù)聲明.

尾遞歸調(diào)用

// 尾調(diào)用
function f(x){
  return g(x);
}
// 非尾調(diào)用
function f(x){
  return g(x) + 1;
}

尾調(diào)用的概念非常簡單,一句話就能說清楚,就是指某個函數(shù)的最后一步是調(diào)用另一個函數(shù)。
函數(shù)調(diào)用自身,稱為遞歸。如果尾調(diào)用自身,就稱為尾遞歸。

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

factorial(50000) // 棧溢出

如果改寫成尾遞歸,只保留一個調(diào)用記錄,復(fù)雜度 O(1) 。

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

factorial(5, 1) // 120
史上最簡單的的數(shù)組去重
---- 原始做法----
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基礎(chǔ)的人,基本知識這里不講解。

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,代執(zhí)行玩ajax后調(diào)用函數(shù)。

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

前面一個是成功回調(diào),后面一個是失敗回調(diào)。

Promise.prototype.then方法返回的是一個新的Promise對象,因此可以采用鏈式寫法。

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

需求來了,聽好了:現(xiàn)在我需要通過ajax去后臺哪一個數(shù)據(jù),數(shù)據(jù)里面有個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對象,都是異步執(zhí)行,過完4s打印如上內(nèi)容,其實就是每個promise返回值的數(shù)組。

其實有了這么多的知識足以在項目中運用自如了。我們來個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的數(shù)據(jù)然后再去查一遍數(shù)據(jù)。

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

我們看到了deleteItem之后,會去在查詢page為1的數(shù)據(jù),之前的deleteItem返回的也是promise可以繼續(xù).then。
好了,今天就到這里了吧。。。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容