详解new()

什么是new()

在 JavaScript 中,new 关键字是用于创建对象实例的操作符。当你在 JavaScript 中使用 new 关键字时,它会执行以下操作:

  1. 创建一个空对象。
  2. 将这个空对象的原型链接到构造函数的原型对象上。
  3. 将构造函数的作用域赋给这个新对象(因此 this 关键字指向这个新对象)。
  4. 执行构造函数中的代码(初始化新对象的属性等)。
  5. 如果构造函数没有明确返回一个对象,则返回新创建的对象实例。

下面是一个简单的 JavaScript 示例,演示了如何使用 new 关键字创建对象实例:

js 复制代码
// 构造函数
function Person(name, age) {
    this.name = name;
    this.age = age;
}

// 使用 new 关键字创建 Person 的实例
var person1 = new Person('Alice', 30);

// person1 现在是 Person 的一个实例
console.log(person1.name); // 输出: Alice
console.log(person1.age);  // 输出: 30

在这个示例中,new Person('Alice', 30) 创建了一个名为 person1 的新对象实例,这个对象具有 nameage 属性,值分别为 'Alice'30

为什么要有new?

在 JavaScript 中,new 关键字的主要作用是创建对象实例。它的存在有几个重要原因:

  1. 实例化对象: 使用 new 关键字可以根据构造函数创建新的对象实例。这允许我们在 JavaScript 中使用面向对象的编程范式,即通过创建对象实例来表示和操作数据。
  2. 继承原型链: new 关键字会确保新创建的对象实例能够正确继承构造函数的原型链。这意味着新对象可以访问构造函数原型对象上定义的方法和属性。
  3. 确保正确的 this 上下文: 使用 new 关键字会确保在构造函数内部使用的 this 关键字指向新创建的对象实例,而不是全局对象。这使得构造函数能够正确地初始化新对象的属性。
  4. 明确标识对象实例: 在 JavaScript 中,new 关键字的存在使得我们能够清晰地区分构造函数和普通函数。通过使用 new 关键字调用构造函数,我们明确地表明正在创建一个新的对象实例。

总的来说,new 关键字的存在使得在 JavaScript 中进行面向对象编程更加方便和清晰,能够创建并正确初始化对象实例,确保原型链继承,并且提供了明确的方式来标识对象实例的创建过程。

如何手写一个new?

new 运算符创建⼀个⽤户定义的对象类型的实例或具有构造函数的内置对象类型之⼀。

先看看 new 实现了哪些功能。

js 复制代码
function Person (name, age) {
    this.name = name;
    this.age = age;
    this.habit = 'Games';
}

Person.prototype.strength = 80;
Person.prototype.sayYourName = function () {
    console.log('I am ' + this.name);
}

var person = new Person('Kevin', '18');
console.log(person.name) // Kevin
console.log(person.habit) // Games
console.log(person.strength) // 60
person.sayYourName(); // I am Kevin

我们可以看到,实例 person 可以:

  1. 访问到 Otaku 构造函数⾥的属性;
  2. 访问到 Otaku.prototype 中的属性;

接下来,我们可以尝试着模拟⼀下了。 因为 new 是关键字,所以⽆法像 bind 函数⼀样直接覆盖,所以我们写⼀个函数,命名为 objectFactory,来模拟 new 的效果。⽤的时候是这样的:

js 复制代码
function Person () {
    ......
}

// 使⽤ new
var person = new Person(......);
// 使⽤ objectFactory
var person = objectFactory(Person, ......)

1. 初步实现

因为 new 的结果是⼀个新对象,所以在模拟实现的时候,我们也要建⽴⼀个新对象,假设这个对象叫obj,因为 obj 会具有 Person 构造函数⾥的属性,我们可以使⽤ Person.apply(obj, arguments) 来给 obj 添加新的属性。

然后,实例的 proto 属性会指向构造函数的 prototype,也正是因为建⽴起这样的关系,实例可以访问原型上的属性

js 复制代码
// 第⼀版代码
function objectFactory() {
    var obj = new Object();
    Constructor = [].shift.call(arguments);
    obj.__proto__ = Constructor.prototype;
    Constructor.apply(obj, arguments);
    return obj;
};

在这⼀版中,我们:

  1. ⽤new Object() 的⽅式新建了⼀个对象 obj;
  2. 取出第⼀个参数,就是我们要传⼊的构造函数。此外因为 shift 会修改原数组,所以 arguments 会被去除第⼀个参数;
  3. 将 obj 的原型指向构造函数,这样 obj 就可以访问到构造函数原型中的属性;
  4. 使⽤ apply,改变构造函数 this 的指向到新建的对象,这样 obj 就可以访问到构造函数中的属性;
  5. 返回 obj;

测试下:

js 复制代码
function Person (name, age) {
    this.name = name;
    this.age = age;
    this.habit = 'Games';
}

Person.prototype.strength = 60;
Person.prototype.sayYourName = function () {
    console.log('I am ' + this.name);
}

function objectFactory() {
    var obj = new Object(),
    Constructor = [].shift.call(arguments);
    obj.__proto__ = Constructor.prototype;
    Constructor.apply(obj, arguments);
    return obj;
};

var person = objectFactory(Person, 'Kevin', '18')
console.log(person.name) // Kevin
console.log(person.habit) // Games
console.log(person.strength) // 60
person.sayYourName(); // I am Kevin

2. 最终实现

假如构造函数有返回值

js 复制代码
function Person (name, age) {
    this.strength = 60;
    this.age = age;
    return {
        name: name,
        habit: 'Games'
    }
}

var person = new Person('Kevin', '18');
console.log(person.name) // Kevin
console.log(person.habit) // Games
console.log(person.strength) // undefined
console.log(person.age) // undefined

在这个例⼦中,构造函数返回了⼀个对象,在实例 person 中只能访问返回的对象中的属性。 ⽽且还要注意⼀点,在这⾥我们是返回了⼀个对象,假如我们只是返回⼀个基本类型的值呢? 再举个例⼦:

js 复制代码
function Person (name, age) {
    this.strength = 60;
    this.age = age;
    return 'handsome boy';
}

var person = new Otaku('Kevin', '18');
console.log(person.name) // undefined
console.log(person.habit) // undefined
console.log(person.strength) // 60
console.log(person.age) // 18

这次尽管有返回值,但是相当于没有返回值进⾏处理。

所以我们还需要判断返回的值是不是⼀个对象,如果是⼀个对象,我们就返回这个对象,如果没有,我 们该返回什么就返回什么。

最终版的代码

js 复制代码
function objectFactory() {
    // 创建一个空对象
    var obj = new Object();
    // 从参数中取出构造函数,并将其从参数列表中删除
    Constructor = [].shift.call(arguments);
    // 将新对象的原型链接到构造函数的原型对象上
    obj.__proto__ = Constructor.prototype;
    // 调用构造函数,并将新对象作为上下文
    var ret = Constructor.apply(obj, arguments);
    // 如果构造函数返回了一个对象,则返回该对象;否则返回新创建的对象
    return typeof ret === 'object' ? ret : obj;
};
相关推荐
霍先生的虚拟宇宙网络3 分钟前
webp 网页如何录屏?
开发语言·前端·javascript
温吞-ing5 分钟前
第十章JavaScript的应用
开发语言·javascript·ecmascript
彪8256 分钟前
第十章 JavaScript的应用 习题
javascript·css·ecmascript·html5
Myli_ing2 小时前
考研倒计时-配色+1
前端·javascript·考研
余道各努力,千里自同风2 小时前
前端 vue 如何区分开发环境
前端·javascript·vue.js
PandaCave2 小时前
vue工程运行、构建、引用环境参数学习记录
javascript·vue.js·学习
软件小伟2 小时前
Vue3+element-plus 实现中英文切换(Vue-i18n组件的使用)
前端·javascript·vue.js
醉の虾2 小时前
Vue3 使用v-for 渲染列表数据后更新
前端·javascript·vue.js
张小小大智慧2 小时前
TypeScript 的发展与基本语法
前端·javascript·typescript
疯狂的沙粒3 小时前
对 TypeScript 中高级类型的理解?应该在哪些方面可以更好的使用!
前端·javascript·typescript