乐趣区

关于前端:原型及原型链

一般函数与构造函数

函数还是之前的函数,惟一的区别就是首字母大写

// 构造函数
function Foo(m, n) {
  let ret = m + n;
  this.m = m;
  this.n = n;
  return ret;
}

// 一般函数
function foo(m, n) {
  let ret = m + n;
  this.m = m;
  this.n = n;
  return ret;
}

一般函数

  • 失常调用,不须要 new 关键字
  • 执行过程还是按着堆栈执行 + 作用域链查找机制
// 一般函数调用
let ret = Foo(10, 20);
console.log(ret);

构造函数

  • 应用 new 关键字调用
  • 执行 new 操作时,浏览器会创立一个空间示意空对象与 this 进行关联,this 指向是新创建的实例
  • 函数体内如果没有 return 或者说 return 的是根本数据类型,默认返回对象实例;函数体内如果返回援用类型,那么就以本人返回为主
  • 函数此时叫做类,返回的后果叫对象实例
// 构造函数执行
let res = new Foo(20, 20);
console.log(res);

new 操作符

  • 失常状况下应用 new 实现对象实例创立,如果以后类不须要传递参数,则能够不加括号运行
  • new Foo, 未加小括号阐明 FOO 不须要传参,称之为无参列表
  • new Foo 与 new Foo() 的优先级不同,前者为 19,后者为 20
  • 每一次 new 都会将函数从新执行,生成一个新的执行上下文,创立一个新的实例对象,因而两个实例对象不一样

重写 new 办法

new 做了什么

  1. 创立实例对象
  2. 执行构造函数,将 this 指向实例对象
  3. 解决返回值

模仿 new 实现

function Person(name) {this.name = name;}
Person.prototype.slogan = function () {console.log("前端界最帅的人");
};
Person.prototype.sayName = function () {console.log(` 我的名字是 ${this.name}`);
};

// let p1 = new Person('zce')
// p1.slogan()
// p1.sayName()

function _new(Ctor, ...params) {
  //01 创立实例对象
  // let obj = {}
  // obj.__proto__ = Ctor.prototype
  let obj = Object.create(Ctor.prototype);

  //02 调用构造函数,扭转 this 指向
  let ret = Ctor.call(obj, ...params);

  //03 解决返回后果
  if (ret !== null && /^(object|function)$/.test(typeof ret)) {return ret;}
  return obj;
}

let p1 = _new(Person, "zce");
p1.slogan();
p1.sayName();
console.log(p1 instanceof Person);

原型及原型链

名词阐明

prototype 属性

  • 每一个函数(除箭头函数)数据类型,都自带一个 prototype 属性,指向原型对象(Function 除外)
  • 每个原型对象自带一个 constructor 属性,指向以后构造函数自身
  • 函数数据类型

    • 一般函数、箭头函数、生成器函数
    • 构造函数(自定义类)
    • 内置函数(内置构造函数)

proto 属性

  • 每一个对象数据类型,都自带一个 proto 属性,(隐式原型)
  • 该属性的值指向所属类的原型对象 prototype
  • 对象数据类型

    • 一般对象、数组对象、正则对象、日期对象
    • prototype 原型对象
    • 实例对象
    • 函数也是对象

Object 类

  • 所有对象都是 Object 内置类的实例
  • Object 也是一个函数,同样具备 prototype 属性,指向本人的原型对象
  • 它的原型也是一个对象,因而具备 proto 属性
  • Object 原型对象的 proto 指向 Null(外部设计)

原型链查找机制

  1. 首先找本人公有的属性,公有中存在就是公有的
  2. 公有中不存在,则默认基于 proto 找所属类的原型对象
  3. 如果类的原型上没有,则基于原型对象的 proto 持续向上查找,直到找到 Object.prototype 为止

示例代码

function Foo() {
  this.m = 10;
  this.n = 24;
  this.getM = function () {console.log(this.m);
  };
}
Foo.prototype.getM = function () {console.log(this.m);
};

Foo.prototype.getN = function () {console.log(this.n);
};

let foo1 = new Foo();
let foo2 = new Foo();
console.log(foo1.getM === foo2.getM);
console.log(foo1.getN === foo2.getN);
console.log(foo1.__proto__.getN === Foo.prototype.getN);
console.log(foo1.__proto__.getM === foo2.getM);
console.log(foo1.getM === Foo.prototype.getM);
console.log(foo1.constructor);
console.log(Foo.prototype.__proto__.constructor);
foo1.getM();
foo1.__proto__.getM();
foo2.getN();
Foo.prototype.getN();

Function 与 Object

函数多种角色

  1. 函数

    1. 一般函数调用(堆栈执行作用域)
    2. 构造函数实例化(原型及原型链)
  2. 对象

    1. 键值对
  3. 三种角色之间没有必然的分割,然而最外围的函数就是函数

语录

  1. Function 是一等公民,在 JS 中存在多种角色,一般函数、构造函数、对象
  2. 每一个对象都存在 proto 属性,指向所属类的原型对象(隐式原型,原型链属性)
  3. 每一个函数都存在 prototype 属性,指向它的原型对象
  4. 所有函数都是 Function 内置类的实例,且 Function 自身也是一个函数
  5. 所有对象都是 Object 的实例,且 Object 自身也是一个函数
  6. Function 与 Object 是二大并行的基类,尽管最终查找落脚点都是 Object 身上
  7. Function.prototype 原型对象是一个匿名函数,尽管它是一个函数,然而它的解决机制和原型对象是一样的,它的 proto 属性指向所属类的原型对象,也就是 Object.prototype

不具备 prototype 属性

  1. Function.prototype 不具备,是一个匿名函数
  2. 对象中应用 ES6 语法定义函数 const obj = {say(){}}
  3. 箭头函数
  4. 不具备 prototype 属性的函数是不能执行 new 操作的
退出移动版