js手写new创建实例过程

  1. new是什么?
    new是js中的关键字,用来通过构造函数新建一个实例对象,如:
const obj = new Object() // {}
function People(name, age){
	// 设置当前实例的属性,作为构造函数时this指向当前要创建的实例
	// 作为普通函数使用时,this指向当前调用者,未指明调用者时,默认指向window(非严格模式下)
	this.name = name
	this.age = age
}
const p = new People('李逍遥', 24) // p {name:李逍遥, age:24}
  1. new创建实例时内部做了哪些事?
    ① 新建一个空对象
    ② 通过Object.setPrototypeOf(source, target)或__proto__,设置空对象的隐式原型为构造函数的原型对象prototype
    ③ 改变构造函数的this指向为当前空对象(这样函数内this才能指向空对象,执行时能对空对象属性赋值),并将传参(设置实例的属性值一系列参数,该演示里指传入的name、age值)传入构造函数中执行
    ④ 若上一步构造函数执行结果有返回对象,则返回该对象,否则返回当前自己创建的对象。
    注: 必须要知道一件事就是: 如果构造函数有返回值且是对象(包括数组),就会将当前对象作为 new 出来的对象,无返回就会是正常新建的实例(即取自己手动创建的对象)。如下演示:
function People(name, age) {
    this.name = name
    this.age = age
    return {
        name: '上官耀',
        age: 29,
        hobbit: 'eat'
    }
}
undefined
var a = new People('叶少伟', 24) // {name: '上官耀', age: 29, hobbit: 'eat'}

代码实现:

// 构造函数
function People(name, age) {
	this.name = name
	this.age = age
	return this // 可不用返回当前实例对象
}
// 手写new关键字
function myNew(fn) {
	const obj = {} // 创建空对象
	Object.setPrototypeOf(obj, fn.prototype) // 设置空对象隐式原型为构造函数的prototype
	const args = Array.prototype.slice.call(arguments, 1) // myNew函数的传参获取及处理
	const newObj = fn.apply(obj, args) // 执行构造函数为obj设置属性值
	return newObj ?? obj // 返回创建的实例对象,完成通过函数实现new关键字功能
}
const mine = myNew(People, '李逍遥', 24) // 等价于 const mine = new People('李逍遥', 24)
console.log(mine) // People {name: '李逍遥', age: 24}

手写 new 简化版:

function myNew(fn, ...arr) {
    let newObj = {}  // 创建空对象
    Object.setPrototypeOf(newObj, fn.prototype)  // 设置空对象隐式原型为构造函数的prototype
    const res = fn.apply(newObj, arr)  // 执行构造函数为 newObj 设置对象成员属性
    return res instanceof Object? res:newObj  // 如果构造函数本身有返回对象,就将该对象作为新建对象,没有就返回自己创建的对象 newObj
}

现在对上面代码可能疑惑的点进行解读:
arguments:这个是ES6新增的一种获取函数传参的方式,它是类数组对象,可自行在控制台打印出来查看,会看到是一个数组展现形式,但是别被误导,展开看是有callee等属性的。(见下图)
Array.prototype.slice.call(arguments, 1):上面说了arguments是类数组,所以需要借助②转换成数组类型,也可以通过ES6新增的数组方法Array.from(arguments)转为数组类型(②是ES6未出现前常用的将类数组转为数组的方法)。②为什么要写成这种形式,还多传了一个1?因为slice是数组原型上的方法,而arguments不是真正意义上的数组,所以没有该方法,不能arguments.slice()处理,得从Array调取slice方法并且通过call改变slice函数内部this指向为当前arguments对其处理,传1是因为arguments[0]第一个元素是传入的构造函数fn也就是People,所以只截取arguments第二个往后的参数,就是要传入的一系列属性值。
return newObj ?? obj??ES6的null/undefined判断运算符,前者是null或undefined就会取后面的值。因为我在构造函数里返回了当前实例,所以这里取得就是newObj,没有返回newObj就是undefined,这时会取obj。很多人看不懂其他人手撕new时,为什么在这里要用newObj instanceOf Object? newObj:obj(和③写法等价),这里就是一种代码强壮性表现,如果你在构造函数时有返回实例,就会取newObj,没有的话就取刚创建的对象obj,有返回时newObj和obj都是同一个实例。
在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值