原型及原型链

普通函数与构造函数

函数还是之前的函数,唯一的区别就是首字母大写
// 构造函数
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);

原型及原型链

原型及原型链_第1张图片

名词说明

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 操作的

你可能感兴趣的:(前端javascript)