new的作用以及自行实现new的原理和一些坑

本文详细解析了JavaScript中new操作符的工作原理,并通过逐步实现,展示了如何手动模拟new操作符的功能,包括处理构造函数中的属性继承及返回值等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

new的作用:实例化并且继承这个对象
一个非常简单的new实例:

function Person( name, age ){  //构造函数
	this.name = name;
	this.age = age;
}
Person.prototype.sex = 'male';	//原型链上的属性
Person.prototype.getName = function(){		//原型链上的方法
	console.log('my name is : ' , this.name)
}

var person = new Person('Jachin', '18')
console.log(person.name);
console.log(person.age);
console.log(person.sex);
person.getName();

在这里插入图片描述
想必大家都非常熟悉new的使用方法,但是自己去实现new过程却并不是一件简单的事情;这其中最重要的就是了解new实例的原理。

Part1:new出来的实例有什么特点

  1. 实例能访问到Person构造函数里面的属性
  2. 实例能访问到Person原型上的属性

解决第一个问题:实例能访问到Person构造函数里面的属性 ( 经典继承 )

function Parent(){
	this.firstName = 'Zhou';
}
function Child(){
	Parent.call(this)
}
var child1 = new Child();
console.log(child1.firstName)

在这里插入图片描述
结合第一个问题的思想解决第二个问题:实例能访问到Person原型上的属性

new函数的第一版本实现

function newFun(){ //模拟new的函数
	var obj = {};		//创建一个空对象
	Constructor = [].shift.call(arguments);		//删除并获取arguments第一个参数
	obj.__proto__ = Constructor.prototype;		//将obj原型链指向构造函数
	Constructor.apply(obj, arguments);		//使用apply改变Constructor的this指向obj,obj就可以访问Constructor中的属性;
	return obj;
}

第一版本的测试用例

function Person( name, age ){  //构造函数
	this.name = name;
	this.age = age;
}
Person.prototype.sex = 'male';	//原型链上的属性
Person.prototype.getName = function(){		//原型链上的方法
	console.log('my name is : ' , this.name)
}

var person = newFun(Person, 'Jachin', '18') //此处使用自定义的newFun
console.log(person.name);
console.log(person.age);
console.log(person.sex);
person.getName();

在这里插入图片描述

重点:由于构造函数可以有return值,所以在使用new实例的时候,构造函数有返回值,并且返回值为对象的话,原型上的属性和方法是不能够被继承的。
在这里插入图片描述
因此如果使用new函数的第一版本实现,是不符合规定的。如图所示
在这里插入图片描述
原型上的方法和属性都被展示出来了,继续改造newFun函数

new函数的第二版本实现 (填坑)
只需要判断Constructor调用obj时候的返回值是否object对象
但是此刻还得堤防 null 这个返回值,因为 typeof null === ‘object’ -> true

function newFun(){ //模拟new的函数
	var obj = {};		//创建一个空对象
	Constructor = [].shift.call(arguments);		//删除并获取arguments第一个参数
	obj.__proto__ = Constructor.prototype;		//将obj原型链指向构造函数
	var result = Constructor.apply(obj, arguments);		//使用apply改变Constructor的this指向obj,obj就可以访问Constructor中的属性;
	return typeof result === 'object' ? result || obj : obj;
}

在这里插入图片描述

Part2:newFun函数里的一些坑

function newFun(){ //模拟new的函数

	// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
	var obj = {};		//创建一个空对象
	// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
	
	Constructor = [].shift.call(arguments);		//删除并获取arguments第一个参数
	obj.__proto__ = Constructor.prototype;		//将obj原型链指向构造函数
	var result = Constructor.apply(obj, arguments);		//使用apply改变Constructor的this指向obj,obj就可以访问Constructor中的属性;
	return typeof result === 'object' ? result || obj : obj;
}

var obj = {} 是书面表达,其实本质上等价于 var obj = new Object() 这种构造表达式
但是我们是要实现new的过程
所以此处不应该用 var obj = {} 来命名一个空对象
因此可以改造为
var obj = Object.create(null)
!!!但是创建出来的这个空对象,是没有prototype这个原型的,因为对象都是没有原型,只有构造函数才有原型,因此此处的null应该改为目标构造函数;

function newFun(){ //模拟new的函数
	Constructor = [].shift.call(arguments);		//删除并获取arguments第一个参数
	var obj = Object.create(Constructor.prototype);		//将对象指向构造函数的原型上
	var result = Constructor.apply(obj, arguments);		//使用apply改变Constructor的this指向obj,obj就可以访问Constructor中的属性;
	return typeof result === 'object' ? result || obj : obj;
}

此处才能大功告成完成一个new的模拟
此处感谢网易大神:肖恒敏的讲解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值