前言
業(yè)界最冷的笑話大概是:沒有對象宝穗?new一個唄逮矛!怎么new一個對象呢?今天我們就來談談JS中晋控,怎樣使用new關鍵字創(chuàng)建一個對象及其實現(xiàn)原理赡译。
下文中涉及到的題外知識點:
- es6-擴展運算符
console.log(...[1, 2, 3])
// 1 2 3
- es6-變量的解構賦值
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
new一個對象
new 操作符后跟一個構造函數(shù)。構造函數(shù)可以是JS原生引用類型(比如:Object综看、Array红碑、Date、Number等)忠寻,也可以是自定義函數(shù)奕剃。
比如這樣
var person = new Object();
var colors = new Array(); // 數(shù)組也是對象喲
或者這樣
function Person(age) {
this.age = age;
console.log(this)
}
var p1 = new Person(20);
實現(xiàn)原理
1柿顶、創(chuàng)建一個空對象
2嘁锯、讓空對象的_proto_(IE沒有該屬性)成員指向了構造函數(shù)的prototype成員對象
3家乘、使用apply調用構造器函數(shù),屬性和方法被添加到 this 引用的對象中
4扑馁、如果構造函數(shù)中沒有返回其它對象腻要,那么返回 this,即創(chuàng)建的這個的新對象趟济,否則顷编,返回構造函數(shù)中返回的對象
具體如下:
function _new() {
let obj= {}; // 創(chuàng)建的新對象
// 第一個參數(shù)是構造函數(shù)
let [constructor, ...args] = [...arguments];
// 執(zhí)行 [[原型]] 連接 ;實際上就是生產(chǎn)了一個新的上下文
obj.__proto__ = constructor.prototype;
// 使用apply在obj作用域中調用構造器函數(shù),屬性和方法被添加到 this 引用的對象即obj中
let result = constructor.apply(obj, args);
if (result && (typeof (result) == "object" || typeof (result) == "function")) {
// 如果構造函數(shù)執(zhí)行的結果返回的是一個對象钮惠,那么返回這個對象
return result;
}
// 如果構造函數(shù)返回的不是一個對象,返回創(chuàng)建的新對象
return obj;
}
結合下面的代碼片段來具體分析下:
代碼片段一
function Person(age){
this.age= age;
console.log(this);
return {age:age};//返回對象
}
Person.prototype.index = 1
var person1 = new Person(20); // 此處相當于var person1=_new(Person, 20)
var person2 = Person(18);
console.log(person1);
console.log(person2);
console.log('p1.index=', person1.index)
console.log('p2.index=', person2.index)
上面 var person1 = new Person(20)和var person1=_new(Person, 20)效果相當。
-
obj._proto_ = constructor.prototype峦筒,即 obj._proto_ = Person.prototype,
將obj的_proto_(隱式原型)指向Person的原型對象,此時obj的原型鏈為:
obj => Person.prototype => Object.prototype => null
因此 執(zhí)行到let result = constructor.apply(target, args)這句代碼時术吗,在obj作用域中調用Person函數(shù),打印出 Person {age: 20}
因為Person函數(shù)返回的是一個對象隘蝎,所以console.log(person1)打印出的就是返回的這個對象:{age: 20},person1.index自然就為undefined
而var person2 = Person(18)曼振,只是簡單的調用了Person函數(shù)冰评,在window全局中執(zhí)行,所以打印出Window對象长已,執(zhí)行console.log(person2)打印出:{age: 18},person2.index為undefined
代碼片段二
function Person(age){
this.age= age;
console.log(this);
// return {age:age};//返回對象
}
Person.prototype.index = 1
var person1 = new Person(20); // 相當于var person1 = _new (Person, 20);
var person2 = Person(18);
console.log(person1);
console.log(person2);
console.log('p1.index=', person1.index)
console.log('p2.index=', person2.index)
代碼片段二和代碼片段一的區(qū)別在于中剩, 片段二中構造函數(shù)Person沒有返回值(返回的不是一個對象结啼,還可以通過直接返回age驗證下效果)郊愧,所以person1接收到的實際上是_new新創(chuàng)建的對象obj属铁,即Person {age: 20}, person1.index則相當于obj.index屬性時,它會先找自身的index屬性盒发,如果找不到,則會順著原型鏈向上找间学,這時會找到People.prototype.index仍律,person1.index的結果是1善涨。
由于Person沒有返回值炕横,所以console.log(person2)結果為undefined膜钓,進而颂斜,打印person2.index時會報錯沃疮。
總結
通過new操作符糯彬,我們可以創(chuàng)建原對象的一個實例對象似扔,而這個實例對象繼承了原對象的屬性和方法,所以new存在的意義在于它實現(xiàn)了javascript中的繼承偶器,而不僅僅是實例化了一個對象。
PS: 本文參考了網(wǎng)上的一些文章加之自己的理解霎苗,若有錯誤或者不準確的地方,歡迎留言糾正检眯,非常感謝刽严!