[深入02] 原型链

2021-07-24  本文已影响0人  woow_wu7

导航

[深入01] 执行上下文
[深入02] 原型链
[深入03] 继承
[深入04] 事件循环
[深入05] 柯里化 偏函数 函数记忆
[深入06] 隐式转换 和 运算符
[深入07] 浏览器缓存机制(http缓存机制)
[深入08] 前端安全
[深入09] 深浅拷贝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模块化
[深入13] 观察者模式 发布订阅模式 双向数据绑定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手写Promise
[深入20] 手写函数

[react] Hooks

[部署01] Nginx
[部署02] Docker 部署vue项目
[部署03] gitlab-CI

[源码-webpack01-前置知识] AST抽象语法树
[源码-webpack02-前置知识] Tapable
[源码-webpack03] 手写webpack - compiler简单编译流程
[源码] Redux React-Redux01
[源码] axios
[源码] vuex
[源码-vue01] data响应式 和 初始化渲染

构造函数的缺点

prototype属性

原型对象

原型链

覆盖 overriding


function A(){} //---------------------- 定义构造函数A
A.prototype = new Array() // ---------- 将A.prototype指向实例数组,那么A的实例就能继承数组原型链上的属性和方法
A.prototype.constructor = A // -------- 修改prototype的同时,也要修改constructor防止意外
const a = new A()
a.push(1)
a instanceof Array // true

constructor

<script>
  function A(){}
  const a = new A()
  console.log(A.prototype.constructor === A) // true
  console.log(a.constructor === A.prototype.constructor) // true,constructor属性在prototype对象上,所以能被实例继承并访问
  console.log(a.constructor === A) // true,constructor的作用是可以确定实例由哪个构造函数产生
  console.log(a.constructor === RegExp) // false
  console.log(a.hasOwnProperty('constructor'), "a.hasOwnProperty('constructor')") // constructor不是实例自身属性,继承的
  console.log(new a.constructor(), 'constructor的另一个作用: 一个实例可以借助constructor新建另一个实例')
  const b = new a.constructor()
  console.log( b instanceof A) // true , 即b是A的其中一个实例
</script>

constructor
constructor表示原型对象和构造函数之间的关联关系,修改prototype,需要同时修改constructor,防止引用出错

<script>
  function A () {
    console.log('A构造函数')
  }
  console.log(A.prototype, '修改之前的A.prototype')
  console.log(A.prototype.constructor, '修改之前的A.prototype.constructor') // A
  console.log(A.prototype.constructor.name, '修改之前的A.prototype.constructor.name => 修改前的constrctor名称')
  A.prototype = {name: 'woow_wu7'}
  console.log(A.prototype.constructor, '修改之后的A.prototype.constructor') // Object
  console.log(A.prototype.constructor.name, '修改之后的A.prototype.constructor.name => 修改后的constrctor名称')
  A.prototype.constructor = A
  console.log(A.prototype.constructor, '修改prototype后,要重新指定A.prototype.constructor=A,防止引用出错')
</script>

instanceof

instanceof

function A(){}
const a = new A()
console.log(a instanceof A, 'instanceof的原理是检查右边构造函数的原型对象是否在左边对象的原型链上')
console.log(A.prototype.isPrototypeOf(a), 'instanceof等价于这样')


var d = new Date();
d instanceof Date
// true
// d既是Date的实例,也是Object的实例
d instanceof Object 
// true
// 因为instanceof检查的是右边构造函数的实例,是否在左边实例对象的原型链上,检查整个原型链
// 所以同一个实例,可能对多个构造函数返回ture



var obj = Object.create(null); // 以null为原型对象创建实例
typeof obj // "object"
Object.create(null) instanceof Object // false

installof可以解决调用构造函数时,忘记加new命令的情况

function A() {
  if(!(this instanceof A)) { // 如果this不是A的实例,说明不是new命令调用的,那么执行new
    return new A()
  }
  else {
    this.name = 'woow_wu7'
  }
}
const a = new A()
console.log(a)

Object.getPrototypeOf

Object.getPrototypeOf
- 获取参数对象的原型对象,是获取原型对象的标准方法

Object.getPrototypeOf(Object.prototype) === null // true
Object.getPrototypeOf(Function.prototype) === Object.prototype // true
Object.getPrototypeOf({}) === Object.prototype // true

Object.setPrototypeOf

const a = {}
const b = {name: 'woow_wu7'}
const res = Object.setPrototypeOf(a, b) // 将b设置成a的原型,注意返回值是a
Object.getPrototypeOf(a) === b // true
console.log(a.name)
console.log(res) // a, 返回值是a
var F = function () {
  this.foo = 'bar';
};

var f = new F();
// 等同于
var f = Object.setPrototypeOf({}, F.prototype);
// 返回值f是第一个参数 {}
// Object.setPrototypeOf({}, F.prototype) => 相当于 {}.__proto__ = F.prototype
F.call(f);

Object.create

手动实现一个 Object.create

Object._create = function(obj) {
  function F(){}
  F.prototype = obj // 新建一个构造函数,将构造函数的prototype指向传入的对象,执行构造函数,即实例的原型指向了传入的对象
  return new F()
}

Object.prototype.isPrototypeOf

Object.prototype.__proto__

获取原型对象的方法比较

- `obj.__proto__` // 只有浏览器才有,不建议使用
- obj.constructor.prototype // 手动修改原型时,可能会失真
- Object.getPrototypeOf() // 推荐的获取方法
直接修改原型对象时,需要同时修改constructor防止失真


function A(){}
const a = new A()

function B() {}
B.prototype = a
const b = new B()

b.constructor.prototype === a // false
// 因为:b.constructor === a.constructor === A.prototype.constructor === A
// 所以:b.constructor.prototype === A.prototype
// 结论:当直接修改prototype属性时,一定要修改constructor属性 !!!!!!!!!!!!!!!!!!(重要)


// 如果是下面这样则:
function A(){}
const a = new A()
function B() {}
B.prototype = a
B.prototype.constructor = B // 修改了prototype,同时修改constructor则引用不会出错
const b = new B()
b.constructor.prototype === a // true

Object.prototype.hasOwnProperty

Date.hasOwnProperty('length') // true
Date.hasOwnProperty('toString') // false

in运算符

例子:
function X(){}
X.prototype.name = 'woow_wu7';
let x = new X()
'name' in X // true
// 因为:in 运算符返回一个布尔值,表示属性是否在对象中存在,不区分自身还是继承
// 所以:'name' in X => 返回 true

for in 和 for of

for in
- 用于数组,i表示:key
- 用于对象,i表示:key
- 用于对象时,for...in会遍历自身属性和继承的属性,
for of
- 用于数组:i表示 value


const objP = {sex: 'man'}
const obj = {name: 'woow_wu7', age: 20, address: 'hangzhou'};
Object.setPrototypeOf(obj, objP)
for(let i in obj) {
  console.log(i, 'for in 循环 => 用于对象,会遍历自身和继承的属性') // name,age,address,sex
  if (obj.hasOwnProperty(i)) {
    console.log(i, '如果只希望遍历自身属性,可以用Object.prototype.hanOwnProperty(属性名)来过滤')// name,age,address
  }
}

易错点总结

- constructor
    - constructor表示构造函数和原型对象之间的关联关系,如果修改了原型对象,需要一起修改构造函数,防止引用出错。
    -(每一个构造函数都有一个prototype属性,prototype的constructor指向prototype所在的构造函数)
- instanceof
    - 原理:instanceof是检查(右边构造函数的prototype属性)是否在(左边对象)的原型链上
    - instanceof失效的情况:
        - 如果一个对象的__proto__属性指向null,则instanceof就会失效
        - 因为右边是构造函数的prototype => 终点是Object.prototype,是否在左边对象的原型链上
        - Object.prototype.__prototo__ === null
        - Object.prototype instanceof Ojbect             // false  
        // Object.create(null) instanceof Object   // false,因为创建的实例没有任何属性和方法,也没有原型
image image

2021/07/24更新

上一篇 下一篇

猜你喜欢

热点阅读