從瞭解new出發,手寫代碼(1)

什麼是new?

new 運算符建立一個用戶定義的對象類型的實例或具備構造函數的內置對象的實例數組

new作了什麼?

  1. 建立一個空的簡單JavaScript對象(即{});
  2. 連接該對象(即設置該對象的構造函數)到另外一個對象 ;
  3. 將步驟1新建立的對象做爲this的上下文 ;
  4. 若是該函數沒有返回對象,則返回this。

new的實現過程

讀懂個人代碼

其實咱們瞭解了new的整個過程咱們是比較容易去實現一個new的 1.let obj = {} 或者 let obj = new object(); 二者等價 2.obj.proto = Func.prototype (Func爲構造函數) 3.經過apply改變this,以訪問函數內部變量 4.返回bash

function New() {
    let obj = {}; // 建立對象
    console.log(arguments);
    let constructor =  [].shift.call(arguments); // 獲取構造函數
    console.log(arguments);
    if (constructor.prototype !== null) {
        obj.__proto__ = constructor.prototype; // 構造函數連接到新對象
    }
    let ret = constructor.apply(obj, [].slice.call(arguments)); // 改變this指向
    console.log(arguments);
    console.log(typeof ret);
    if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
        return ret;
    }
    return obj; // 若是函數沒有返回對象類型Object(包含Functoin, Array, Date, RegExg, Error),那麼new表達式中的函數調用將返回該對象引用。
}

function name(a, b) {
	this.a = a;
	this.b = b;
}

let c = New(name, 1, 2)
let d = new name(1, 2)
console.log(c);
console.log(d);
複製代碼

咱們來看返回值:app

咱們看到New 和 new產生了一樣的效果函數

注意:[].shift.slice()會改變咱們的arguments。typeof null == "object"性能

重點:爲何要進行對象判斷,通常實現new時會把這一點的解釋給忽略。優化

默認狀況下函數的返回值爲undefined(即沒有顯示地定義返回值的話),可是構造函數比較例外,new構造函數在沒有return的狀況下默認返回新建立的對象。可是在有顯示返回值的狀況下,若是返回值爲基本數據類型的話(string,number,boolean,undefined,null),返回值仍然爲新建立的對象,這一點比較奇怪,須要注意。只有在顯示返回一個非基本數據類型的對象的時候,函數的返回值才爲指定的對象。在這種狀況下,this值所引用的對象就被丟棄了。ui

看下面兩個例子:this

例1: spa

例2: prototype

// return; // 返回 this

// return null; // 返回 this

// return this;

// return []; // 返回 []

// return function(){}; // 返回 這個 function,拋棄 this

// return false; // 返回 this

// return new Boolean( false); // 返回新 boolean;拋棄 this

// return 'hello world'; // 返回 this

// return new String( 'hello world'); // 返回 新建的 string,拋棄 this

// return 2; // 返回 this

// return new Number( 32); // 返回新的 number,拋棄 this

arguments是什麼?

arguments 是一個對應於傳遞給函數的參數的類數組對象。

arguments 對象只能在函數內使用

[].slice.call()作了什麼?

將arguments轉換成數組

相似的轉換方法

var args = Array.prototype.slice.call(arguments);

var args = [].slice.call(arguments);

// ES2015

const args = Array.from(arguments);

const args = [...arguments];

MDN上不建議咱們對參數進行slice 解決方案:

function New() {
    let obj = {}; // 建立對象
    console.log(arguments);
    let constructor =  [].shift.call(arguments); // 獲取構造函數
    console.log(arguments);
    if (constructor.prototype !== null) {
        obj.__proto__ = constructor.prototype;
    }
    let ret = constructor.apply(obj, (arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments)));
    console.log(ret);
    console.log(arguments);
    if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
        return ret;
    }
    return obj;
}

function name(a, b) {
	this.a = a;
	this.b = b;
}

let c = New(name, 1, 2)
let d = new name(1, 2)
console.log(c);
console.log(d);
複製代碼

對參數使用slice會阻止某些JavaScript引擎中的優化。若是你關心性能,嘗試經過遍歷arguments對象來構造一個新的數組。另外一種方法是使用被忽視的Array構造函數做爲一個函數

[].shift.call()作了什麼?

獲取arguments的第一個參數,改變arguments的length

slice作了什麼

返回一個新的數組,包含從 start 到 end (不包括該元素)的 arrayObject 中的元素。

.slice()方法

定義和用法 slice(start, end) 方法可提取數組的某個部分,並以新的數組返回被提取的部分。

使用start(包含) 和 end(不包含)

參數來指定提取數組開始和結束的部分。

若是未指定start和end,則返回整個數組。

若是指指定一個參數,該參數做爲start使用,返回包括start位置以後的所有數組。

若是是負數,則該參數規定的是從數組的尾部開始算起的位置。也就是說,-1 指數組的最後一項,-2 指倒數第二個項,以此類推。

call作了什麼(具體實現會將call,apply,bind放在一塊兒講)

改變[]中的this指向

apply作什麼(具體實現會將call,apply,bind放在一塊兒講)

改變this的指向

this.指向如何改變

經過 apply指向 apply將構造函數的this指向新建立的對象

將具備length屬性的對象轉換爲數組的方法

array.form() 或者 ... 或則遍歷

array.from轉換時須要注意什麼

類數組對象的key值爲數字

slice如何實現?

Array.prototype.slice = function(start,end){
     var result = new Array();
     start = start || 0;
     end = end || this.length; //this指向調用的對象,當用了call後,可以改變this的指向,也就是指向傳進來的對象,這是關鍵
     for(var i = start; i < end; i++){
          result.push(this[i]);
     }
     return result;
複製代碼
相關文章
相關標籤/搜索