面试官:能否三行代码实现JS的New关键字

谁能不相思,独在机中织。

探索

凡实践,需理论先行,在开始之前,我们要先具体了解一下new创建对象的具体过程。

new的this指向

或者说构造函数的this指向,先来看一个小的示例,思考一下,log打印出来的是什么?

JS 复制代码
function Person(name, age) {
  this.name = name;
  this.age = age;
}

let person = new Person("后俊生", 18);
console.log(person.name);   //后俊生
console.log(person.age);    //18

很显然,大家都知道打印出来的分别是"后俊生", 18,那么,你有没有思考过这样的简单问题,为什么打印出来的是这些数据?

->我明明把参数传递给了构造函数Person,而不是实例person?参数为什么会附加到实例上边去了?

OK,带着这些思考,我们将代码稍稍改动,思考一下,打印出来的会是什么?

JS 复制代码
function Person(name, age) {
  this.name = name;
  this.age = age;
}

let person = new Person("后俊生");
console.log(person.name);   //后俊生
console.log(person.age);    //undefined

结果是"后俊生", undefined,我们把函数中this赋值语句注释,实例中的属性就没了,好像这两句话是给实例赋值的?是不是有了一些眉目了?

既然this.name = name是给实例person复制的,那么是不是this.name就是person.name,是不是this = person

bingo~,恭喜你,答对了,

构造函数中的this,指向的是实例本身!!!

构造函数的原型

我们将代码继续改造,向他的原型链上添加数据

JS 复制代码
function Person(name, age) {
  this.name = name;
  this.age = age;

  function logIfo() {
    console.log(age, 1);
    return 1;
  }
}

Person.prototype.habit = "Games";
Person.prototype.sayHi = function() {
  console.log("Hi " + this.name);
};

let person = new Person("后俊生", 18);
console.log(person.name);   //后俊生
console.log(person.age);    //18
console.log(person.habit);  //Games
person.sayHi();             //Hi 后俊生

由上面的代码,不难发现,当函数被使用new创建的时候,构造函数的原型链上的数据也会被添加到实例上。

返回值

以上都是没有返回值的情况,那么,如果函数有返回值呢?

那么我们将代码再次改造一下:

javascript 复制代码
function Person(name, age) {
  this.name = name;
  this.age = age;

  return {
    hair: 'black',
    gender: 'man'
  }
}

let person = new Person("后俊生", 18);
console.log(person.name);   //undefined
console.log(person.age);    //undefined
console.log(person.hair);  //black
console.log(person.gender);  //man

我们发现,实例person上不存在name、age属性了,只包含返回对象的属性,好像我们构造的是返回对象的实例,那么,真的是这样吗?

再来看看这个代码

JS 复制代码
function Person(name, age) {
  this.name = name;
  this.age = age;

  return 1;
}

let person = new Person("后俊生", 18);
console.log(person.name);   //后俊生
console.log(person.age);    //18

咦?什么情况,为什么这次又存在name、age属性了?

事实上: 如果函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用会自动返回这个新的对象。

new方法思路

我们来总结一下new方法做的事情:

  1. 改边this,指向实例
  2. 将构造函数的原型复制到实例上
  3. 根据返回值类型决定实例的属性

这就是我们的new方法需要实现的功能,

最终实现:

之前写过一下实现方式,功能一样,但是不够优雅,这是我见过最优雅的解决方案,三行代码解决问题

JS 复制代码
function _new(fn, ...arg) {
  //以一个现有对象作为原型,创建一个新对象,继承fn原型链上的属性
  const obj = Object.create(fn.prototype);
  // 使用 apply,改变构造函数 this 的指向到新建的对象,这样 obj 就可以访问到构造函数中的属性
  const ret = fn.apply(obj, arg);
  // 如果函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用会自动返回这个新的对象。
  return ret instanceof Object ? ret : obj;
}

测试

我们来做一下测试:

JS 复制代码
function Person(name, age) {
  this.name = name;
  this.age = age;

  return {
    hair: 'black',
    gender: 'man'
  }
}

let person =  _new(Person,"后俊生", 18);
console.log(person.name);   //undefined
console.log(person.age);    //undefined
console.log(person.hair);  //black
console.log(person.gender);  //man
JS 复制代码
function Person(name, age) {
  this.name = name;
  this.age = age;

  return 1;
}
Person.prototype.habit = "Games";

let person =  _new(Person,"后俊生", 18);
console.log(person.name);   //后俊生
console.log(person.age);    //18
console.log(person.habit);    //Games

发现,和我们使用new方法的结果一模一样,至此,new方法实现完成。

注意

这里的_new方法只能传入函数,不能传入class,因为class在使用apply时会报错。

JS 复制代码
const ret = fn.apply(obj, arg);
                 ^

TypeError: Class constructor Person cannot be invoked without 'new'

引用

面试官问:能否模拟实现JS的new操作符 - 掘金

Object.create() - JavaScript | MDN

github.com/mqyqingfeng...

相关推荐
OPHKVPS17 分钟前
VoidStealer新型窃密攻击:首例利用硬件断点绕过Chrome ABE防护,精准窃取v20_master_key
前端·chrome
前端摸鱼匠21 分钟前
【AI大模型春招面试题13】残差连接(Residual Connection)与层归一化(Layer Norm)在Transformer中的作用?
人工智能·深度学习·语言模型·面试·transformer·求职招聘
月光宝盒造梦师24 分钟前
Ant Design Ellipsis 中的判断逻辑 isEleEllipsis 方法非常消耗性能
javascript·react·优化
gechunlian8837 分钟前
SpringBoot3+Springdoc:v3api-docs可以访问,html无法访问的解决方法
前端·html
驾驭人生1 小时前
ASP.NET Core 实现 SSE 服务器推送|生产级实战教程(含跨域 / Nginx / 前端完整代码)
服务器·前端·nginx
酉鬼女又兒1 小时前
零基础快速入门前端ES6 核心特性详解:Set 数据结构与对象增强写法(可用于备赛蓝桥杯Web应用开发)
开发语言·前端·javascript·职场和发展·蓝桥杯·es6
慧一居士1 小时前
Vue项目中,子组件调用父组件方法示例,以及如何传值示例,对比使用插槽和不使用插槽区别
前端·vue.js
我是伪码农2 小时前
HTML和CSS复习
前端·css·html
林恒smileZAZ2 小时前
前端实现进度条
前端
前端老石人2 小时前
邂逅前端开发:从基础到实践的全景指南
开发语言·前端·html