Skip to content
字数
903 字
阅读时间
4 分钟

概述

new操作符 作为经典面向对象语言的一大特征,其在 JS 中也有相应的实现
在 java 中,new 是通过调用类的构造器来返回这个类的 实例;而在 JS 中,构造器的概念由 prototype对象 来抽象代表

——在 JavaScript 中, new操作符 用于创建一个给定构造函数的实例对象的例子

js
eg:
function Person(name, age) {
	this.name = name;
	this.age = age;
}
Person.prototype.sayName = function () {
	console.log(this.name)
}
const person1 = new Person('Tom', 20)
console.log(person1) // Person {name: 'Tom', age: 20}
person1.sayName() // 'Tom'

构造函数带基本类型返回值

js
eg:
function Test(name) {
  this.name = name
  return 1
}
const t = new Test('xxx')
console.log(t.name) // 'xxx'

构造函数中返回一个原始值,然而这个返回值并没有作用

构造函数带引用类型返回值

js
eg:
function Test(name) {
  this.name = name
  console.log(this) // Test { name: 'xxx' }
  return { age: 26 }
}
const t = new Test('xxx')
console.log(t) // { age: 26 }
console.log(t.name) // 'undefined'

构造函数如果返回值为一个对象,那么这个返回值会被正常使用

实现流程

调用 new 的流程如下:

  1. 创建一个新的空对象
  2. 新对象的原型 (prototype) 设置为构造函数的原型对象
  3. 将构造函数的 this 设置为创建的新的对象
  4. 判断构造函数的返回值类型,如果为基本类型,则返回创建的对象,如果为引用类型,则返回引用类型的对象

其中涉及到的知识点:

如何将新对象的原型对象设置为构造函数的原型对象

第二步:如何将新对象的原型对象设置为构造函数的原型对象
想当然的使用:obj.prototype = constructor.prototype 是不行的,有很多局限性,如 obj 一开始得初始化,确保存在 prototype属性

使用 Object.create() ,创建一个新对象,使用现有的对象来提供新创建的对象的 proto
可以看作以下简短代码的实现:

js
function create(obj) {
  function F() {}
  F.prototype = obj
  return new F()
}

API 使用具体参考:Object.create() - JavaScript | MDN

回到问题,我们使用 newObject = Object.create(constructor.prototype); 来实现功能

如何更改 this 的作用域

使用 call 或者 apply,二者的区别也就是方法第二个参数一个为 一系列的参数,一个为 类数组

作用可以参考以下用例

js
function People(name, age) {
    this.name = name;
    this.age = age;
}

function Student(name, age, grade) {
    People.call(this, name, age);
    this.grade = grade;
}

var student = new Student('小明', 21, '大三');
console.log(student.name + student.age + student.grade);//小明21大三

API 使用具体参考:Function.prototype.call() - JavaScript | MDN

回到问题,我们使用 constructor.apply(newObject, arguments),该操作将 constructor 的 this 作用为 newObject

具体实现

js
function objectFactoty() {
	let newObject = null;
	// 调用 Array的原型方法 shift() ,拆解arguments参数为 构造函数 + 传参
	let construct = Array.prototype.shift.call(arguments);
	// 判断构造函数是否是一个函数
	if (typeof constructor != "function") {
		console.error('type error');
		return;
	}
	// 将新对象的原型设置为构造器的原型对象
	newObject.prototype = Objecte.create(constructor.prototype);
	// 改变this作用域
	result = constructor.apply(newObject, arguments);
	// 判断返回对象,若存在返回值,则判断是否为引用类型返回值,
	// 若不是引用类型返回值,则不提供返回对象
	let flag = result && (typeof result === "object" || typeof result === "function");
	// 返回结果
	return flag ? result : newObject;
}

// 使用举例
objectFactory(function(name, age) {
	this.name = name;
	this.age = age;
}, "Tom", 20)

参考

Object.create() - JavaScript | MDN
Function.prototype.call() - JavaScript | MDN
浅谈JS中call()和apply()的区别和用途

贡献者

The avatar of contributor named as jiechen jiechen

页面历史

撰写