JavaScript 模擬new的實現

首先一句話解釋一下new:

new 能夠實現一個對象繼承構造函數的屬性以及方法數組

舉個例子:bash

function Parent(name,age){
  this.name = name;
  this.age = age;
}

Parent.prototype.sayName = function(){
  console.log(`my name is ${this.name}`)
}

let child = new Parent('js',18);

console.log(child.name);    // js
console.log(child.age);     // 18
child.sayName();            // my name is js
複製代碼

從這個案例,能夠看到實例childapp

1.繼承了構造函數裏面的屬性函數

2.能夠訪問到構造函數prototype中的屬性測試

經過下面這個圖來直觀感覺一下,這裏涉及到js原形以及原型鏈,先提早補腦一下 ui

proto 指的是實例對象的原形鏈,每一個對象都有這個屬性,它只想構造函數的原形;而prototype屬性是函數對象纔有的屬性,那什麼是函數對象?就是凡是經過new functioin()出來的就是函數對象。

child.constructor === Parent //true

child._proto === Parent.prototype //true

Parent.prototype.constructor === Parent //true
複製代碼

接下來按照剛剛的例子結合上面的這張圖片來實現new 內部是怎麼的實現過程:this

function newFactory(){
	let obj = new Object(),
	    context = Array.prototype.shift.call(arguments); 
	    
	 obj.__proto__ = context.prototype;
	context.apply(obj,arguments);
	return obj;
}
複製代碼
  1. 首先經過new Object()新建一個對象 obj;
  2. 取出第一個參數,就是咱們要傳入的構造函數。由於 shift 會修改原數組,因此 arguments 會被去除第一個參數.
  3. 講obj的原形指向構造函數
  4. 使用apply改變構造函數的this指向,這樣obj就能夠訪問到構造函數的屬性

接下來測試一下spa

function Parent(name,age){
  this.name = name;
  this.age = age;
}

Parent.prototype.sayName = function(){
  console.log(`my name is ${this.name}`)
}

function newFactory(){
  let obj = new Object(),
      context = Array.prototype.shift.call(arguments); 
	    
  obj.__proto__ = context.prototype;
  context.apply(obj,arguments);
  return obj;
}
let child = newFactory(Parent,'js','18')

console.log(child.name);    // js
console.log(child.age);     // 18

child.sayName();            // my name is js
複製代碼

到這一步咱們已經完成了80%,由於還有種狀況咱們沒有考慮到,當構造函數有返回值的時候,實例化的對象會怎麼?prototype

function Parent(name,age){
  this.age = age;
  this.sex = 'boy'
  return {
     name:name,
     address:'china'
  }
}

var child = new Parent('js','18')

console.log(child.name);    // js
console.log(child.age);     // undefined
console.log(child.sex);    // undefined
console.log(child.address);     // china
複製代碼

經過上面這個案例,能夠看出當構造函數返回了一個對象的化,實例child只能訪問到返回對象的屬性,那若是返回的是基本類型呢?code

function Parent(name,age){
  this.age = age;
  this.sex = 'boy'
  return 'china';
}

var child = new Parent('js','18')

console.log(child.name);    // undefined
console.log(child.age);     // 18
console.log(child.sex);    // boy
console.log(child.address);     // undefined
複製代碼

從這個案例能夠看出來當構造函數返回值是基本類型的時候,跟沒有返回值同樣。

終極版 四大步驟:

一、建立一個空對象,而且 this 變量引用該對象,// let obj = new Object();

二、繼承了函數的原型。// obj.proto = func.prototype;

三、屬性和方法被加入到 this 引用的對象中。並執行了該函數func// func.call(target);

四、新建立的對象由 this 所引用,而且最後隱式的返回 this 。// 若是func.call(target)返回的res是個對象或者function 就返回它

function newFactory(){
  let obj = new Object(),
      context = Array.prototype.shift.call(arguments); 
    
   obj.__proto__ = context.prototype;
   let res = context.apply(obj,arguments);
 
   if ((typeof res === "object" || typeof res === "function") && res !== null) {
      return res;
  }
  return obj;
}

複製代碼
相關文章
相關標籤/搜索