JavaScript进阶3--对象原型

系列文章:算法

对象

在 JavaScript 中,共有6种类型主要类型:数组

  • string
  • number
  • boolean
  • null:typeof null 时会返回字符串"object",这是语言自己的一个bug。
  • undefined
  • object:包括函数、数组、内置对象等在内的子类型。就是键/值对的集合。typeof function 时会返回字符串"function"。

内容

对象的内容是由一些存储在特定命名位置(任意类型的)值组成的,咱们称之为属性。浏览器

⚠️注意:即便在对象的文字形式中声明一个函数表达式,这个函数都不会“属于”一个对象,他们只是对于相同函数的多个引用bash

属性描述符

从 ES5 开始,全部的属性都具有属性描述符:闭包

  • writable:是否能够修改属性的值。值为false时,至关于定义了一个空操做setter。函数

  • configurable:若是属性是可配置的,就可使用 defineProperty(...) 进行修改属性描述符。configurable的值设置为false是单向操做,没法撤销!post

    ⚠️注意:即便 configurable:false,仍是能够把 writable 的值从 true 改为 false,但不能够由 false 改为 true。测试

  • enumerable:控制的是属性是否会出如今对象的属性枚举中。ui

⚠️注意:属性不必定包含值——也多是包含getter/setter的“访问描述符”this

[[Get]] 和 [[Put]]

[[Get]]

属性访问时,对象默认的内置 [[Get]] 操做首先在对象中查找是否有名称相同的属性,遍历可能存在的[[prototype]]链,若是不管如何也没找到名称相同的属性,则返回值 undefined

⚠️注意:访问变量时,没找到会抛出一个 ReferenceError 异常。刚问对象属性时,没找到会返回 undefined

[[Put]]

当给属性赋值:

  • 先检查对象中是否存在这个属性,若是有这个属性, [[Put]]算法大体会检查一下内容:
    1. 属性是否有setter,若是有的话,调用 setter。
    2. 属性的描述符中 writable 的值若是是false,如下两种状况:
      • 严格模式下,抛出 TypeError 异常。
      • 非严格模式下,静默失败。
    3. 若是都不是,则将该值设置为属性的值。
  • 若是对象没有这个属性,[[prototype]]链会被遍历,若是 [[prototype]] 上找到了这个属性。
    1. 若属性的 writable: true,就会在对象里添加一个新属性。
    2. 若属性的 writable: false,
      • 严格模式下,会抛出错误
      • 非严格模式下,会静默失败
    3. 若属性有setter,那么就会调用这个setter,属性不会被添加到对象上。
  • 若是 [[prototype]] 上找不到这个属性,这个属性会直接添加到对象上。

原型

JavaScript 中的对象有一个特殊的 [[prototype]] 内置属性,是对其余对象的引用。全部普通的 [[prototype]] 最终都会指向内置的 Object.prototype。

全部的函数默认都会拥有一个名为 prototype 的公有且不可枚举的属性,它会指向另外一个对象,这个对象被称为原型,这个对象默认有一个公有且不可枚举的属性.constructor,这个属性引用的是对象关联的函数。

[[prototype]]机制就是指对象中的一个内部连接引用另外一个对象。

原型继承

JavaScript 会在两个对象之间建立一个关联,这样一个对象就能够经过委托来访问另外一个对象的属性和函数。

var bar = Object.create(foo); 会建立一个对象 bar 并把它关联到 foo。

调用 Object.create(...)会凭空建立一个“新”对象,并把新对象内部的[[prototype]] 关联到指定的对象...,这样会直接把原始的关联对象抛弃掉。

有如下几种方法检查两个对象之间的关系:

  • instanceof:用于测试函数的 prototype 属性是否出如今对象的原型链中任何位置。这个方法只能处理对象和函数之间的关系。
  • prototypeObj.isPrototypeOf(object): 检查 prototypeObj 是否在 object 的原型链中出现。
  • getPrototypeOf: 返回指定对象的原型

⚠️注意:绝大多数(不是全部)的浏览器支持__proto__ 这个非标准的方法访问内部的[[prototype]],__proto__存在于内置 Object.prototype 中,看起来像个属性,其实更像一个 getter/setter。

好题练习

练习题1:

function Test() {
    Test.prototype.add = num => {
      this.number = num
    }
    Test.prototype.number = 0
  }

t1 = new Test()
t2 = new Test()
t1.add(12)
var a = t1.number
var b = t2.number
console.log(a, b) // => 0,12
复制代码

每实例化一次Test,Test.prototype.add都会被从新赋值一次,而且赋值的函数为箭头函数,箭头函数无自身的上下文,其中的this指向Test上下文中的this,即每次实例化的实例对象。因此,t2 实例化后 Test.prototype.add 里的 this 指向t2,t1 调用 add 函数时,给 t2 的number 赋值。

练习题2:

var A = function() {};
A.prototype.n = 1;
var b = new A();
A.prototype = {
  n: 2,
  m: 3
}
var c = new A();

console.log(b.n);  // 1
console.log(b.m);  // undefined

console.log(c.n);  // 2
console.log(c.m);  // 3
复制代码

⚠️注意:JavaScript 中的引用和其余语言中的引用/指针不一样,不能指向别的变量或者引用,只能指向

b 在实例化过程当中,b.__proto__ = A.prototype,此时 A.prototype = {n: 1},当 A.prototype = { n: 2, m: 3 }时,打破原型链,A.prototype 引用了新内存地址的对象,b.__proto__指向的依旧是旧内存地址的对象。若是是 A.prototype.n = 2; A.prototype.m = 3; 就不会指向新的引用。

练习题3:

var F = function() {};

Object.prototype.a = function() {
  console.log('a');
};

Function.prototype.b = function() {
  console.log('b');
}

var f = new F();

f.a();  // a
f.b();  // TypeError: f.b is not a function

F.a(); // a
F.b(); // b
复制代码

f的原型链:f => f.__proto__ => F.prototype => F.prototype.__proto__ => Object.prototype => Object.prototype.__proto__ => null

F的原型链:F => F.__proto__ => Function.prototype => Function.prototype.__proto__ => Object.prototype => Object.prototype.__proto__ => null

练习题4:

function Person(name) {
    this.name = name
}
let p = new Person('Tom');

question1: p.__proto__等于什么? // p.__proto__ === Person.prototype
question2: Person.__proto__等于什么? // Person.__proto__ === Function.prototype
复制代码

以为有收获的,点个赞再走吧~

相关文章
相关标签/搜索