Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

深入js之造new轮子 #4

Open
FE-Sadhu opened this issue Apr 8, 2019 · 0 comments
Open

深入js之造new轮子 #4

FE-Sadhu opened this issue Apr 8, 2019 · 0 comments
Labels
深入js笔记 about javascript

Comments

@FE-Sadhu
Copy link
Owner

FE-Sadhu commented Apr 8, 2019

先搞清楚new

首先,我们要知道,创建一个用户自定义对象需要两步:

  1. 通过编写函数来定义对象类型。
  2. 通过new来创建对象实例。

通过此处引出了new的描述,new是干嘛的呢?引用MDN的一句话:

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

初看时可能会有点懵,我们可以对这段话修枝剪叶留下枝干: new运算符可以创建 对象实例 。(之后再通过new的运用,补全其枝叶。)

这句话就符合最开头说的创建一个用户自定义对象的第二步了。实际应用也是,new运算符经常是与一个函数结合使用。

来看个例子:

// example
function Foo(name, age) {
  this.name = name;
  this.age = age;
  this.sex = 'male';
}
Foo.prototype.brother = "宇智波鼬";

Foo.prototype.chat = function () {
  console.log('how are you? 鸣人');
}

var person = new Foo('佐助', 21);

console.log(person.name);
console.log(person.sex);
console.log(person.brother);
person.chat();
// 佐助
// male
// 宇智波鼬
// how are you? 鸣人

从这个例子中我们可以看出,:

  1. 生成了新对象(实例)person。
  2. Foo函数里的this指向该实例对象person。
  3. 这个对象实例可以访问Foo.prototype上的属性。

MDN上帮我们总结的更清楚,当代码 new Foo(...) 执行时,会发生以下事情:

  1. 一个继承自 Foo.prototype 的新对象被创建。("继承"用得不是很准确,应该叫委托才好,具体参见you-dont-konw-js)
  2. 使用指定的参数调用构造函数 Foo ,并将 this 绑定到新创建的对象。new Foo 等同于 new Foo(),也就是没有指定参数列表,Foo 不带任何参数调用的情况。
  3. 由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)

注: 由同一个构造函数创建的实例各个独立且其原型相同,原型对象都等于 构造函数.prototype === 实例.__proto__

第一步

有了对new的认识后,咱们开始仿写。因为 new 是个关键字,没法像之前的call、bind那样子写方法覆盖。我们可以写个方法 copyNew()。

以上面例子为例,我们让 new Foo(xxx) 的效果等于 copyNew(Foo, xxx)就行。

来第一步代码:

// codes
function copyNew() {
  var obj = new Object();

  var constructor = [].shift.call(arguments); // 取出第一个构造函数参数。注意shift可以改变原数组。

  obj.__proto__ = constructor.prototype; // 这样obj就可以访问在构造函数的prototype上的属性

  // 根据apply经典继承来让函数的this指向实例
  constructor.apply(obj, arguments);

  return obj
}

have a test:

// example
function Foo(name, age) {
  this.name = name;
  this.age = age;
  this.sex = 'male';
}
Foo.prototype.brother = "宇智波鼬";

Foo.prototype.chat = function () {
  console.log('how are you? 鸣人');
}

var person = copyNew(Foo, '佐助', 21);

console.log(person.name);
console.log(person.sex);
console.log(person.brother);
person.chat();
// 佐助
// male
// 宇智波鼬
// how are you? 鸣人

第一步成功。

第二步

我们现在考虑下构造函数有返回值的情况。

1、假设构造函数的返回值是对象。

举个例子:

// 假设构造函数的返回值是对象
function Foo(name, age) {
  this.brother = '宇智波鼬';
  this.age = age
  return {
    habit: 'coding',
    name: name
  }
}

const person = new Foo('佐助', 21);

console.log(person.age);
console.log(person.brother);
console.log('**********华丽的分割线**********');
console.log(person.habit);
console.log(person.name);
// undefined
// undefined
//**********华丽的分割线**********
// coding
// 佐助

我们可以发现,构造函数Foo的person实例完全只能访问Foo返回的对象中的属性。

那构造函数的返回值不是对象呢? 我们试一个基本类型的值。

2、假设构造函数的返回值是基本类型的值

// 假设构造函数的返回值是一个基本类型的值
function Foo(name, age) {
  this.brother = '宇智波鼬';
  this.age = age
  return 'sadhu'
}

const person = new Foo('佐助', 21);

console.log(person.age);
console.log(person.brother);
console.log('**********华丽的分割线**********');
console.log(person.habit);
console.log(person.name);
// 21
// 宇智波鼬
//**********华丽的分割线**********
// undefined
// undefined

结果刚刚相反,相当于无返回值时的处理。

所以我们可以根据这两个例子的结果去完善最后的代码,根据这个思路:

若当构造函数返回值是对象时,则new调用后也返回该对象实例。若当构造函数的返回值是基本类型或者无返回值时,都当作无返回值情况处理,该返回什么就返回什么。

最终代码:

function copyNew() {
  var obj = new Object();
  var constructor = [].shift.call(arguments);
  obj.__proto__ = constructor.prototype;
  var result = constructor.apply(obj, arguments);
  return typeof result === 'object' ? result : obj;
}

参考:

  1. MDN
  2. JavaScript深入之new的模拟实现
@FE-Sadhu FE-Sadhu added the 深入js笔记 about javascript label Apr 8, 2019
@FE-Sadhu FE-Sadhu changed the title JS复习笔记之造new轮子 深入JS笔记之造new轮子 Apr 18, 2019
@FE-Sadhu FE-Sadhu changed the title 深入JS笔记之造new轮子 深入js之造new轮子 Apr 21, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
深入js笔记 about javascript
Projects
None yet
Development

No branches or pull requests

1 participant