new 可以實現一個物件繼承建構函式的屬性以及方法
舉個例子:
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
複製程式碼
從這個案例,可以看到例項child
1.繼承了建構函式裡面的屬性
2.可以訪問到建構函式prototype中的屬性
通過下面這個圖來直觀感受一下,這裡涉及到js原形以及原型鏈,先提前補腦一下
proto 指的是例項物件的原形鏈,每個物件都有這個屬性,它只想建構函式的原形;而prototype屬性是函式物件才有的屬性,那什麼是函式物件?就是凡是通過new functioin()出來的就是函式物件。child.constructor === Parent //true
child._proto === Parent.prototype //true
Parent.prototype.constructor === Parent //true
複製程式碼
接下來按照剛剛的例子結合上面的這張圖片來實現new 內部是怎麼的實現過程:
function newFactory(){
let obj = new Object(),
context = Array.prototype.shift.call(arguments);
obj.__proto__ = context.prototype;
context.apply(obj,arguments);
return obj;
}
複製程式碼
- 首先通過new Object()新建一個物件 obj;
- 取出第一個引數,就是我們要傳入的建構函式。因為 shift 會修改原陣列,所以 arguments 會被去除第一個引數.
- 講obj的原形指向建構函式
- 使用apply改變建構函式的this指向,這樣obj就可以訪問到建構函式的屬性
接下來測試一下
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%,因為還有種情況我們沒有考慮到,當建構函式有返回值的時候,例項化的物件會怎麼?
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只能訪問到返回物件的屬性,那如果返回的是基本型別呢?
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
複製程式碼
從這個案例可以看出來當建構函式返回值是基本型別的時候,跟沒有返回值一樣。
終極版 四大步驟:
1、建立一個空物件,並且 this 變數引用該物件,// let obj = new Object();
2、繼承了函式的原型。// obj.proto = func.prototype;
3、屬性和方法被加入到 this 引用的物件中。並執行了該函式func// func.call(target);
4、新建立的物件由 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;
}
複製程式碼