【重学】原型链

作者: woow_wu7 | 来源:发表于2019-06-23 15:20 被阅读0次

大纲:
构造函数
手写new命令
原型链
Object对象的相关方法

(x)易错点

  • constructor表示构造函数和原型对象之间的关联关系,如果修改了protype属性,要同时修改constructor属性,防止引用出错
var P = function () {};
var p = new P();

var C = function () {};
C.prototype = p;  -------- C.prototype修改成了p,所以c.constructor === p.constructor === P.prototype.constructor,
var c = new C();

c.constructor.prototype === p // false --------------- 要想是p,就需要指定c.constructor = C
c.constructro.prototype === P.prototype // true 
(1) 实例只能访问构造函数prototype上的属性,不能直接修改构造函数的prototype属性
(2) 如果要修改,只能通过 x.constructor.prototype.color='yellow'方式修改

function A() {}
A.prototype.color = 'red'
const x = new A()
const y = new A()
x.color = 'yellow' ------------- 因为x.color是给x本身添加属性并赋值,而不是修改x的原型对象的属性
y.color?????  // 'red'
  • instanceof
1. instanceof原理:检查 ( 右边构造函数的prototype属性 ) 是否在 ( 左边对象的原型链上 )

2. instanceof失效的情况:
  - 如果一个对象的__proto__是null,instanceof就会判断失效
  - Object.prototype.__prototo__ === null
  - Object.prototype instanceof Ojbect       // false  
  // Object.create(null) instanceof Object   // false,因为创建的实例没有任何属性和方法,也没有原型

(1)构造函数

  • 函数名大写,this代表将要生成的实例,使用new命令生成实例
  • new命令:执行构造函数,返回实例对象(new命令本身就可以执行构造函数,所以后面的括号可以不要)
  • 如何保证构造函数和new命令一起使用?(忘记使用new了)
    1. 在构造函数内部使用严格模式:use strict,在严格模式下this不能指向window,默认是undefined,undefined不能添加属性,就会报错
    2. 在构造函数内部判断是否使用了new命令
  • new命令的原理?
    1. 生成一个空对象(作为要返回的实例对象)
    2. 将空对象的隐式原型指向构造函数的prototype属性
    3. 将构造函数中的this指向新生成的空对象
    4. 执行构造函数
  • 手写new命令
前置知识:

1. new命令返回值
  - new命令总是返回一个对象,要么是( 实列对象 ),要么return后面所跟的 (对象)

2. 构造函数中的return语句,使用new命令时的返回情况
  - return 一个原始类型的值,使用new命令返回的是:-------------------------- this对象
  - return 一个对象,使用new命令返回的是:--------------------------------- return后面紧跟的那个对象,而不是this对象
  - 如果构造函数中没有return语句,则默认返回的是:-------------------------- this对象

3. 如何判断一个数据的数据类型?
  - typeof:返回 number,string,boolean,undefined,symbol,ojbect,function(七种)
  - instanceof:- 原理是检测 ( 右边构造函数的显示原型prototype )是否在左边 ( 实列对象的原型链上 )
                - instanceof只能用于对象,不能用于原始类型的值
  - Object.prototype.toString().call(参数) ----------- 返回"[object 类型]"
  - js中的数据类型 number, string, boolean, null, undefined, symbol, object (七种)

4. arguments对象
  - 返回实参组成的类似数组的对象,该对象具有length属性
 
5. 如何把arguments对象转换成真正的数组
  - Array.from(arguments)
  - Array.prototype.slice.call(arguments)

6. new命令的原理
  - 新建一个空对象,作为要返回的实列对象
  - 将空对象的隐式原型__proto__指向构造函数的显示原型prototype
  - 将构造函数中的this指向空对象
  - 执行构造函数

7. 如何新建一个对象
  - Object.create(b) : 以参数对象为原型,生成实列对象
  - new Object()
  - 对象字面量
  - 注意:const a = Object.create(b) 即可以实现 a.__proto__ === b == 构造函数.prototype

8. push unshift  和  pop shift
- push unsift 向数组添加数据,改变原数组,有参数,返回值是添加过后的数组长度 ( 不是返回添加的值,因为参数中已经知道 )
- pop shift 删除数据,改变原数组,没有参数,所以返回值是删除的数据值

9. new命令实行后的效果:
  - 生成的对象可以访问构造函数的属性
  - 生成的对象可以访问构造函数prototype上的属性

10. 手写new命令

function Constructor(name, age) {
  this.name = name
  this.age = age
  // 构造函数如果执行new命令,默认返回的是this对象
  // 这里模拟的是new命令,所以手动返回this对象

  // 如果返回其他对象,不是this对象,需要在构造函数返回值中做判断
  return this
}
Constructor.prototype.address = function() {
  console.log('shanghai')
}
function _new() {
  const obj = new Object()
  // 等于 const obj = {}
  // 等于 const obj = Object.create(null)       -------   以null为原型,生成实例对象

 const paramsConstructor =  Array.prototype.shift.call(arguments)
  // 等于 [].prototype.shift.call(arguments)
  // 等于 Array.prototype.shift.apply(arguments)
  // shift 删除数组的第一个参数,改变原数组,返回被删除的值 ---------- push, unshift 返回被添加删除的值
  // unshift 在数组的最前面添加数据,返回添加过后数组的长度 ---------- pop, shift 返回更新后的数组长度
  // 把arguments对象中的构造函数参数取出
  
  obj.__proto__ = paramsConstructor.prototype
  // 将空对象的隐式原型指向构造函数的显示原型

  const res = paramsConstructor.apply(obj, arguments)
  // 将构造函数中的this指向空对象
  // 空对象就能访问构造函数中的属性
  // apply第二个参数是数组,但是类似数组的对象也可以,会转化成数组

  return /Object/.test(Object.prototype.toString.call(res)) ? res : obj
  // 如果构造函数的返回的是一个和this无关对象则返回该对象,即res(this对象也返回)
  // 如果返回不是对象,就返回this对象,即空对象ojb
}
const b =  _new(Constructor, 'wu', 10)
console.log(b)

1.https://github.com/mqyqingfeng/Blog/issues/13

  1. https://segmentfault.com/a/1190000014452865

  2. https://segmentfault.com/a/1190000015276659

  • new命令可以用Object.setPrototypeOf()方法来模拟
function A() {
  this.name = 'wang'
}
const a = new A()


----

const resa = Object.setPrototypeOf({}, A.prototype) --------------- 将A.prototype设置成空对象的原型对象
A.call(resa) ------------------------------------------------------ 将构造函数中的this指向空对象,并执行构造函数

(2) 原型链

  • 原型对象的作用: 定义所有实列对象所共享的属性和方法
  • 所有对象都是 Objecr.prototype 的实例,所有对象都有toString和valueOf方法,因为都继承了Object.prototype上的这两个方法
  • Object.prototype.__proto__ === null null没有任何属性和方法,也没有自己的原型,原型链到此中止
  • constructor属性定义在prototype上,所以construstor能被所有实例对象所共享
    a.constructor === A.prototype.constructor = A
  1. constructor属性的作用
  • constructor的作用就是得知:实例对象到底是由哪个构造函数产生的
  • 有了constructor属性,就可以从一个实例新建另一个实例

特殊的原型链

特殊的原型链:

结论:
(1) 所有 ( 函数对象 ) 都是 ( Function构造函数 ) 的实例
  - 所以 Function.__proto__ === Function.prototype
  - 所以 fn.__protot__ === Function.prototype
  - 所以 Function instanceof Function  // true

(2) 所有函数的prototype对象都是Obect构造函数的实例(!但是Object除外)
  - Function.prototype instanceof Object // true
  - function.prototype.__proto__ === Object.prototype
  - Object.protype instanceof Object  // false

(3)
Function instanceof Function   ------------------- true
Function instanceof Object     ------------------- true
Object instanceof Object       ------------------- true
Object instanceof Function     ------------------- true
原型链 20170503152146141.png

(3)Object对象的相关方法

1. Object.getPrototypeOf

Object.getPrototypeOf()返回参数对象的原型对象,是获取原型对象的标准方法

Object.getPrototypeOf(params) 返回参数对象的原型对象,是获取原型对象的标准方法

    function A() {}
    const a = new A()
    const res = Object.getPrototypeOf(a)
    console.log(a.__proto__ === res) // true
    console.log(A.prototype === res) // true

2. Object.setPrototypeOf(origin, pro)

将第二个参数设置成第一个参数的原型,返回该参数对象即第一个对象

Object.setPrototype(a,b) 将b对象设置成a对象的原型对象,返回值是a对象

    const a = {}
    const b = {name: 'wang'}
    const res = Object.setPrototypeOf(a, b)
    console.log(res) // {}
    console.log(Object.getPrototypeOf(a)) // {name: 'wang'}

Object.setPrototypeOf(a, b)可以用来模拟new命令

function A() {
  this.name = 'wang'
}
const a = new A()


----


const resa = Object.setPrototypeOf({}, A.prototype) --------------- 将A.prototype设置成空对象的原型对象
A.call(resa) ------------------------------------------------------ 将构造函数中的this指向空对象,并执行构造函数

3. Object.create()

Object.create()以参数对象为原型,返回实例对象

以下生成对象的方式等价
Object.create({})
Object.create(Object.prototype)
new Object()

如果想要生成不继承任何属性的对象,可以用如下写法

var obj = Object.create(null)


obj.valueOf()
// TypeError: Object [object Object] has no method 'valueOf'

因为obj的原型是null,所以obj不具备 Object.prototype上挂在的属性和方法,比如 valueOf,toString

Object.create()参数必须是个对象,不能为空或者不是对象

4. Object.prototype.isPrototypeOf

实例对象的isPrototypeOf用来判断该对象是否为参数对象的原型

var o1 = {};
var o2 = Object.create(o1); // o1是o2的原型对象
var o3 = Object.create(o2); // o2是o3的原型对象

o2.isPrototypeOf(o3) // true ---------------------- 实例对象的isPrototypeOf用来判断该实例对象是否是参数对象的原型
o1.isPrototypeOf(o3) // true

相关文章

网友评论

    本文标题:【重学】原型链

    本文链接:https://www.haomeiwen.com/subject/yynmqctx.html