js中 new的操作

js在new()过程中到底做了什么?
要创建 Person 新实例,必须使用 new 操作符。以这种方式调用构造函数实际上会经历以下 4个步骤:
1、创建一个新对象;
2、将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
3、执行构造函数中的代码(为这个新对象添加属性) ;
4、返回新对象。

new 操作符
基于上面的例子,我们执行如下代码

var obj = new Base();

代码的结果,在Javascript引擎中看到的对象模型是:
在这里插入图片描述
new操作符具体干了什么呢?其实就是三件事:
1、创建一个空对象obj;

var obj  = {
    
    };

2、将这个空对象的__proto__ 成员指向Base函数prototype成员对象;

obj.__proto__ = Base.prototype;

3、将Base函数对象的this指针替换成obj,然后再调用Base函数,于是我们就给obj对象赋值了一个id成员变量,这个成员变量的值是”base”,关于call函数的用法。

Base.call(obj);

如果我们给Base.prototype的对象添加一些函数会有什么效果呢?
例如代码如下:

Base.prototype.toString = function() {
    
    
   return this.id;
}

那么当我们使用new创建一个新对象的时候,根据__proto__的特性,toString这个方法也可以做新对象的方法被访问到。于是我们看到了:
构造子中,我们来设置‘类’的成员变量(例如:例子中的id),构造子对象prototype中我们来设置‘类’的公共方法。于是通过函数对象和Javascript特有的__proto__与prototype成员及new操作符,模拟出类和类实例化的效果。



下面的例子中分别通过构造函数与class类实现了一个简单的创建实例的过程。

// ES5构造函数
var Person = function(name,age){
    
    
	this.name = name;
	this.age = age;
}
Person.prototype.sayName = function(){
    
    
	console.log(this.name)
}
var obj = new Person('奶泡森',2);
obj.sayName();// 奶泡森

// ES6 class类
class Person2{
    
    
	constructor(name,age){
    
    
		this.name = name;
		this.age = age;
	}
	sayName(){
    
    
		console.log(this.name)
	}
}
var obj = new Person2('Mayday',21)
obj.sayName();// Mayday

这篇文章主要围绕两点展开,第一,js中new一个对象时到底发生了什么,第二,知道了原理后我们通过js来实现一个简单的new方法。
一、new操作中发生了什么?
比较直观的感觉,当我们new一个构造函数,得到的实例继承了构造器的构造属性(this.name这些)以及原型上的属性。
当我们new一个构造器,主要有三步:
• 创建一个空对象,将它的引用赋给 this,继承函数的原型。
• 通过 this 将属性和方法添加至这个对象
• 最后返回 this 指向的新对象,也就是实例(如果没有手动返回其他的对象)

改写上面的例子,大概就是这样:

// 构造函数
var Person = function(name,age){
    
    
	//1、创建一个空对象,赋给 this,这一步是隐形的,
	// var this = {};
	//2、给 this指向的对象赋予构造属性
	this.name = name;
	this.age = age;
	//3、如果没有手动返回对象,则默认返回 this指向的这个对象,也是隐形的
	//return this;
}
var obj = new Person();

在工作中看过类似下述代码中的操作,将 this赋予一个新的变量(例如_this),最后返回这个变量:

// 构造函数
var Person = function(name,age){
    
    
	var _this = this;
	_this.name = name;
	_this.age = age;
	return _this;
}
var obj = new Person('奶泡森',2);

为什么要这么写呢?我在前面说this的创建与返回是隐性的,但在工作中为了让构造过程更易可见与更易维护,所以才有了上述使用that代替this,同时手动返回that的做法;这也验证了隐性的这两步确实是存在的。

但上述这个解释我觉得不够完美,它只描述了构造器属性是如何塞给实例,没说原型上的属性是如何给实例继承的。下面解释一下:
1、以构造器的 prototype属性为原型,创建新对象;
2、将 this(也就是第一步创建的新对象)和调用参数传递给构造器,执行;
3、如果构造器没有手动返回对象,则返回第一步创建的对象;
new过程中会新建对象,此对象会继承构造器的原型与原型上的属性,最后它会被作为实例返回

二、实现一个简单的 new方法

var Person = function(name,age){
    
    
	this.name = name;
	this.age = age;
};
Person.prototype.sayName = function(){
    
    
	console.log('name:'+this.name)
}
//自己定义的 new 方法
var myNew = function(Parent,...rest){
    
    
	//1.以构造器的 prototype属性为原型,创建新对象;
	var obj = Object.create(Person.prototype);
	//2.将 this 和调用参数传递给构造器执行;
	Person.apply(obj,rest);
	//3.返回第一步的对象;
	return obj;
};
//创建实例,将构造函数 Person与形参作为参数传入
var obj = myNew(Person,'SHE',19);
obj.sayName();// name:SHE

// 最后检验,与使用 new的效果相同
obj instanceof Person;// true
obj.hasOwnProperty('name');// true
obj.hasOwnProperty('age');// true
obj.hasOwnProperty('sayName');// false

猜你喜欢

转载自blog.csdn.net/weixin_44711440/article/details/107405317