天天看點

如何實作一個new方法

如何實作一個方法起到與new相同的作用

首先我們先看看

new

方法做了什麼,又具有什麼特性:

new

的過程中發生了下面這些事:

1、建立一個對象;

2、将構造函數作用域賦到建立的對象中(是以this将會指向這個新對象);

3、執行構造函數中代碼;

4、傳回這個對象

new

關鍵字與構造函數組合使用生成一個執行個體,例如:

new

組合構造函數生成了新的執行個體,這個新的執行個體與構造函數具有以下關系,其實也就是原型中的關系:

person.__proto__ == Person.prototype;
Person.prototype.constructor == Person;
Person.prototype.__proto__ == Object.prototype;
Person.prototype.__proto__.__proto__ == null;
           

上面的關系中後面與

Object

相關的關系我們可以先不管,因為所有原型都是

Object

的執行個體;

新的方法如下:

function _new(Constru,arguments){
}
const person = _new(Person,arguments)
           

通過反推,

1、首先

_new

接受一個構造函數,傳回一個對象,那麼我們就需要在

_new

中先建立一個對象:

function _new(Constru){
	let obj = Object.create(null);
	return obj
}
           

2、傳入的構造函數

Constru

與傳回的對象

obj

需要具有執行個體與構造函數的關系,即:

Constru.prototype = obj.__proto__
           

是以我們_new方法将這個關系進行綁定,如下:

function _new(Constru){
	let obj = Object.create(null);
	obj.__proto__=Constru.prototype;
	return obj
}
           

3、将建立的obj去替換Constru的作用域

function _new(Constru,...arguments){
	let obj = Object.create(null);
	obj.__proto__ = Constru.prototype;
	Constru.call(obj,...arguments)
	return obj;
}
           

4、以上步驟就基本實作了new的操作,但是構造函數有時候會有一些奇怪的使用方法,比如構造函數會在最後傳回一個基本類型值或引用類型值。

對于基本類型的傳回值,在構造函數生成執行個體後不會去理會他;

對于引用資料類型的傳回值,生成執行個體後執行個體能夠調用的屬性就隻有傳回的引用類型的屬性。

例如:

// 引用類型
function Person(name,age){
	this.name  = name;
	this.age = age;
	this.getName = function(){
		console.log(this.name)
	};
	this.setName = function(_name){
		this.name = _name
	}
	return {
		name:this.name,
		age:this.age,
		setName:this.setName
	}
}

const person = new Person("xxx",20);
person.name;    // xxx
person.getName();  // Uncaught TypeError: person.getName is not a function
person.setName("fff"); 
person.name;  // fff

// 基本類型
function Person(name,age){
	this.name  = name;
	this.age = age;
	this.getName = function(){
		console.log(this.name)
	};
	this.setName = function(_name){
		this.name = _name
	}
	return "hello"
}

const person = new Person("xxx",20);
person.name;    // xxx
person.getName();  // xxx
           

是以可以針對這種特殊情況再完善一下

_new

方法,在将構造函數作用域指派給新的對象後,檢測它的傳回值,如果是

Object

類型則将傳回構造函數的傳回值。完善後的方法如下:

function _new(Constru,...arguments){
	let obj = Object.create(null);
	obj.__proto__ = Constru.prototype;
	const result = Constru.call(obj,...arguments);
	return result instanceof Object ? result : obj;
}
           

5、值得注意的是,

new

一個構造函數生成的執行個體一般來說應該是構造函數的執行個體,也就是說能夠通過instanceof檢測結果應該為

true

instanceof的檢測原理就是構造函數的原型對象是否在執行個體的原型鍊上

之前的寫法隻是将構造函數的原型對象指派給了obj的原型對象,這并不能達到繼承的目的,是以可以考慮在建立

obj

時就将構造函數的原型綁定到

obj

Object.create()是一種名叫原型式的繼承方法

此處通過封裝後就相當于組合繼承式繼承方法

function _new(Constru,...arguments){
	let obj = Object.create(Constru.prototype);
	obj.__proto__ = Constru.prototype;
	const result = Constru.call(obj,...arguments);
	return result instanceof Object ? result : obj;
}
           

檢測結果

// 構造函數無傳回值
function Person(name,age){
	this.name  = name;
	this.age = age;
	this.getName = function(){
		console.log(this.name)
	};
	this.setName = function(_name){
		this.name = _name
	}
}
const person = _new(Person,"aaa",20);
person.getName(); // aaa
person instanceof Person // true
person.__proto__ == Person.prototype, // true
person.__proto__.constructor // Person方法

// 構造函數有傳回值
function Person(name,age){
	this.name  = name;
	this.age = age;
	this.getName = function(){
		console.log(this.name)
	};
	this.setName = function(_name){
		this.name = _name
	}
	return {
		name:this.name,
		age:this.age,
		setName:this.setName
	}
}
const person = _new(Person,"aaa",20);
person.getName(); // error
person.setName("bbb");
person.name // bbb
// 構造函數傳回了一個Object執行個體的對象,并不是我麼在_new中建立的obj,那關于obj的關系綁定此對象也一定無法讀到了
person instanceof Person // false
person.__proto__ == Person.prototype, // false
person.__proto__.constructor // Object方法

           

繼續閱讀