资讯专栏INFORMATION COLUMN

JavaScript高级程序设计-摘要笔记-4

zr_hebo / 1873人阅读

摘要:思路是,使用原型链对原型属性和方法进行继承,借用构造函数实现对实例属性的继承。注意使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率,这一点与构造函数模式类似。无论什么情况下都会调用两次超类型的构造函数。

说明:

此摘要笔记系列是我最近看《JavaScript高级程序设计(第3版)》随手所记。
里面分条列举了一些我认为重要的、需要记下的、对我有帮助的点,是按照我看的顺序来的。
摘要笔记本身没有系统性,没有全面性可言,写在这里供有一定基础的前端开发者参考交流。
里面的知识点、例子大部分来源于本书,或者延伸出来的,都经过我的测试是对的,但是没办法保证100%正确,如果有人看到错误的地方,希望指出来,谢谢。

关于继承 1. 原型链概述
function Grandpa () {
  this.name = "Grandpa"
}
Grandpa.prototype.sayName = function () {
  console.log(this.name)
}
function Parent () {
  this.name = "Parent"
}
Parent.prototype = new Grandpa()
Parent.prototype.sayHello = function () {
  console.log("hello")
}
var ming = new Parent()
var li = new Parent()
ming.sayName() // "Parent"
ming.sayHello() // "hello"
ming.sayHello === li.sayHello // true
ming.sayName === li.sayName // true
ming instanceof Parent // true
ming instanceof Grandpa // true
ming.constructor === Parent // false 因为在上面重新了 Parent 的原型,而且没有重新指定 constructor
ming.constructor === Grandpa // true 因为上面没有重写 Grandpa 的原型
ming.__proto__ === Parent.prototype // true
ming.__proto__ === Grandpa.prototype // false
Parent.prototype.__proto__ === Grandpa.prototype // true
2. 确定原型和实例的关系

两种方法: instanceof 操作符和 isPrototypeOf(),
如上例:

Parent.prototype.isPrototypeOf(ming) // true
Grandpa.prototype.isPrototypeOf(ming) // true
Object.prototype.isPrototypeOf(ming) // true
3. 给原型添加方法的代码一定放在替换原型的语句之后,否则无效 4. 原型链的问题
问题1. 引用类型的继承在多个实例之间会共享,无论是原型中添加的属性还是构造函数中添加的属性。如:
function Grandpa () {
  this.friends = ["ming", "li"]
}
function Parent () {
}
Parent.prototype = new Grandpa()
var ming = new Parent()
var li = new Parent()
ming.friends.push("wang")
console.log(ming.friends) // ["ming", "li", "wang"]
console.log(li.friends) // ["ming", "li", "wang"]
ming.friends === li.friends // true
问题2. 在创建子类型的实例中,不能向超类型的构造函数中传递参数
6. 借用构造函数(或叫伪造对象或经典继承)

可以解决引用类型的继承在多个实例之间共享的问题
如:

function Grandpa () {
  this.friends = ["ming", "li"]
}
function Aunt () {
  Grandpa.call(this)
}
var ming = new Aunt()
var li = new Aunt()
ming.friends // ["ming", "li"]
ming.friends.push("wang")
li.friends // ["ming", "li"]
ming.friends // ["ming", "li", "wang"]
7. 借用构造函数可以解决不能向超类型的构造函数中传递参数的问题。

如:

function Grandpa (name) {
  this.name = name
}
function Parent (name, age) {
  Grandpa.call(this, name)
  this.age = age
}
var ming = new Parent("ming", 15)
ming.age // 15
ming.name // "ming"

注意:这种情况,为了避免超类型的构造函数(Grandpa)重写子类型(Parnt)的属性,应在调用超类型构造函数后,再添加应该在子类型中定义的属性

8. 借用构造函数的问题。

问题1. 函数没有复用,占用内存多。
问题2. 超类型原型中定义的方法,对子类型而言是不可见的,结果只能使用构造函数模式。

9. 组合继承

有时候也叫伪经典继承。思路是,使用原型链对原型属性和方法进行继承,借用构造函数实现对实例属性的继承。
如:

function Grandpa (name) {
  this.name = name
  this.friends = ["li"]
}
Grandpa.prototype.sayName = function () {
  console.log(this.name)
}
function Parent (name, age) {
  Grandpa.call(this, name)
  this.age = age
}
Parent.prototype = new Grandpa()
Parent.prototype.sayAge = function () {
  console.log(this.age)
}
var ming = new Parent("ming", 12)
var wang = new Parent("wang", 15)
ming.name // "ming"
ming.friends // ["li"]
ming.friends.push("zhang")
wang.friends // ["li"]
ming.sayName() // "ming"
ming.sayAge() // 12
ming.sayName === wang.sayName // true
ming.sayAge === wang.sayAge // true
ming instanceof Parent // true
ming instanceof Grandpa // true
10. 原型式继承
function object (o) {
  function F () {}
  F.prototype = o
  return new F()
}
实际上,object() 对传入其中的对象执行了一次浅拷贝。如:
var person = {
  name: "wang",
  friends: ["li"]
}
var person1 = object(person)
person1.name // "wang"
person1.friends // ["li"]
person1.name = "zhang"
person.name // "wang"
var person2 = object(person)
person1.friends.push("yang")
person2.friends // ["li", "yang"]
person2.friends === person.friends // true
person1.friends === person.friends // true

这种原型式继承,必须有一个对象作为另一个对象的基础。把基础对象传入 object() 函数,把得到的对象根据具体需求做修改即可。
但是别忘了所有得到的对象引用类型是共享的。

es5 新增了 Object.create() 方法规范了原型式继承。

这个方法接受两个参数,一个用作新对象的原型对象,一个(可选的)为新对象定义额外属性的对象。
在传入一个参数的情况下, Object.create() 和 object() 的行为相同。
Object.create()的第二个参数与 Object.defineProperties() 的第二个参数格式相同:每个属性都是通过自己的描述符来定义的。
这种方式指定的任何属性都会覆盖原型对象上的同名属性。
如:

var person = {
  name: "wang",
  friends: ["li"]
}
var person1 = Object.create(person, {
  name: {
    value: "zhang"
  },
  friends: {
    value: ["a", "b"]
  }
})
person1.name // "zhang"
person1.friends // ["a", "b"]
person.friends // ["li"]
// 支持 Object.create() 的浏览器有 IE 9+,Firefox 4+, Safari 5+, Opera 912+, Chrome.
11. 寄生式继承

寄生式继承是与原型式继承紧密相关的一种思路,和工厂模式类似。如:

function createAnother (original) {
  var clone = object(original)
  clone.sayHi = function () {
    console.log("hi")
  }
  return clone
}
var person = {
  name: "wang",
  friends: ["li"]
}
var person1 = createAnother(person)
person1.friends // ["li"]
person1.sayHi() // "hi"

新的对象person1不仅具有person的所有属性和方法,还有自己的 sayHi() 方法。
示范的 object() 函数不是必须的,任何能够返回新对象的函数都适用于此模式。
注意:使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率,这一点与构造函数模式类似。

12. 组合继承的问题。

无论什么情况下都会调用两次超类型的构造函数。一次是在创建子类型原型的时候,一次是在子类型构造函数内部。
如:

function Grandpa (name) {
  this.name = name
  this.friends = ["ming"]
}
Grandpa.prototype.sayName = function () {
  console.log(this.name)
}
function Parent (name, age) {
  Grandpa.call(this, name)
  this.age = age
}
Parent.prototype = new Grandpa() // 第一次调用超类型的构造函数 Grandpa(),
// 此时原型上有一个friends属性(值是["ming"])和name属性(值undefined,因为没有传参)
Parent.prototype.sayHi = function () {
  console.log("hi")
}
var wang = new Parent("wang", 15) // 第二次调用超类型的构造函数 Grandpa(),在 Parent() 函数内部调用,
// 这一步的操作, Parent 的实例 wang 的属性会覆盖 Parent 的原型上的同名属性 name
12. 寄生组合式继承

基本模式如下:

function inheritPrototype (subType, superType) {
  var prototype = object(superType.prototype)
  prototype.constructor = subType
  subType.prototype = prototype
}
function Grandpa (name) {
  this.name = name
  this.friends = ["ming"]
}
Grandpa.prototype.sayName = function () {
  console.log(this.name)
}
function Parent (name, age) {
  Grandpa.call(this, name)
  this.age = age
}
inheritPrototype(Parent, Grandpa) // 这里没有调用超类型的构造函数 Grandpa() ,但是这里调用了一个函数内部的构造函数
Parent.prototype.sayHi = function () {
  console.log("hi")
}
var li = new Parent("li", 20) // 调用超类型的构造函数 Grandpa(),在 Parent() 函数内部调用

这种模式的好处是只调用一次 Grandpa() 构造函数,因此避免了在 Parent.prototype 上创建不必要的、多余的属性。
作者讲,开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。

继承部分全部结束

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/91656.html

相关文章

  • JavaScript高级程序设计摘要笔记-1

    摘要:说明此摘要笔记系列是我最近看高级程序设计第版随手所记。摘要笔记本身没有系统性,没有全面性可言,写在这里供有一定基础的前端开发者参考交流。对每一项运行给定函数,返回该函数会返回的项组成的数组。是的反操作是的反操作第一部分结束。 说明: 此摘要笔记系列是我最近看《JavaScript高级程序设计(第3版)》随手所记。 里面分条列举了一些我认为重要的、需要记下的、对我有帮助的点,是按照我看...

    chavesgu 评论0 收藏0
  • JavaScript高级程序设计摘要笔记-3

    摘要:如果重设构造函数的原型对象,那么,会切断新的原型对象和任何之前已经存在的构造函数实例之间的联系,它们引用的仍然是最初的原型。说明返回的对象与构造函数或者与构造函数的原型属性没有关系。 说明: 此摘要笔记系列是我最近看《JavaScript高级程序设计(第3版)》随手所记。里面分条列举了一些我认为重要的、需要记下的、对我有帮助的点,是按照我看的顺序来的。摘要笔记本身没有系统性,没有全面性...

    AndroidTraveler 评论0 收藏0
  • JavaScript高级程序设计摘要笔记-6

    摘要:关于对象定义了全局对象。支持的浏览器有除了接受要序列化的对象外,还可以接受另外两个参数。如果是数值,则表示每个级别缩进的空格数,最大,超过的值自动转换成。字符串长度超过,结果中将只出现前个字符。会在结果字符串中插入换行符提高可读性。 关于JSON 1. JSON 对象 es5 定义了全局对象 JSON。支持的浏览器有 IE8+ 、Firefox 3.5+ 、Safari 4+、Chro...

    Batkid 评论0 收藏0
  • JavaScript高级程序设计摘要笔记-5

    摘要:函数表达式和闭包函数声明的一个重要特征是函数声明提升如递归递归函数是在一个函数通过名字调用自身的情况下构成的。注意中已经是块级作用域了,所以这些东西感觉实际用途没有那么大,但是对理解闭包对作用域链中的属性的引用,这一点还是有作用的。 函数表达式和闭包 1. 函数声明的一个重要特征是函数声明提升 如: sayHi() function sayHi () { console.log(h...

    JerryWangSAP 评论0 收藏0
  • JavaScript高级程序设计摘要笔记-2

    摘要:说明此摘要笔记系列是我最近看高级程序设计第版随手所记。其中,描述符对象的属性必须是设置其中一个或多个值,可以修改对应的特性值。如支持的浏览器,可以取得指定属性的描述符。 说明: 此摘要笔记系列是我最近看《JavaScript高级程序设计(第3版)》随手所记。里面分条列举了一些我认为重要的、需要记下的、对我有帮助的点,是按照我看的顺序来的。摘要笔记本身没有系统性,没有全面性可言,写在这里...

    roland_reed 评论0 收藏0

发表评论

0条评论

zr_hebo

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<