资讯专栏INFORMATION COLUMN

读书笔记--对象、实例、原型、继承

LuDongWei / 1403人阅读

创建对象的设计模式

工厂模式

抽象了创建具体对象的过程,用函数封装以特定接口创建对象的细节

解决了创建多个相似对象的问题,没有解决对象识别的问题

function createPerson(name,age){
  var obj = new Object();
  obj.name = name;
  obj.age = age;
  obj.sayName = function(){
    alert(this.name)
  }

return obj;
}

var person = createPerson("aa",12);

构造函数模式

构造函数可以用来创建特定类型的对象,可以用instanceof检测类型

使用new操作符会经历以下四个步骤

创建一个新对象

将构造函数的作用域赋给新对象(因此this指向了这个新对象)

执行构造函数中的代码(为这个新对象添加属性)

返回新对象

缺点:

每个方法都要在每个实例上重新创建一遍

每个方法(函数)就是一个对象,不同实例上的同名函数是不相等的

将函数抽离到外部使多个对象共共享全局作用域的函数虽然可以解决上面问题,但让this.sayName = sayName全局方法会破坏了对象的封装性

function Person(name,age){
  this.name = name;
  this.age = age;
  this.sayName = function(){
    alert(this.name)
  }
}

var person = new Person("aa",12);

person instanceof Person => true
原型模式

prototype

只要创建了一个新函数,就会为函数创建一个prototype属性,这个属性指向函数的原型对象

包含了特定类型的所有对象实例共享的方法和属性

prototype是通过调用构造函数而创建的那个对象实例的原型对象

默认情况下所有原型对象会获得一个constructor(构造函数属性),这个属性指向prototype属性所在函数的指针

当调用构造函数创建一个新实例时,内部将包含一个指针,指向原型对象,连接实例与原型对象

function Person(name,age){
 
}
Person.prototype.name = name;
Person.prototype.age = age;
Person.prototype.sayName = function(){
    alert(this.name)
  }
var person = new Person("aa",12);

person instanceof Person => true

判断原型对象

Person.prototype.isPropertyOf(person) => true

Object.getPropertyOf(person) == Person.prototype => true (es5)

多个对象实例共享原型所保存的属性和方法的基本原理

每当代码读取某个对象的某个属性时,搜索从对象实例本身开始,如果在实例中找到具有给定名字的属性则返回该属性的值

如果没有找到则继续搜索指针指向的原型对象,如果在原型对象中找到给定名字的属性,则返回这个值

虽然可以通过对象实例访问保存在原型实例中的值,但是不能通过对象实例重写原型中的值,如果在实例中添加一个与原型实例中的一个属性同名,访问时实例属性会屏蔽原型中这个属性的值,只能访问到这个实例上属性的值。可以通过delete操作符删除实例属性,重新访问原型中的属性

使用hasOwnProperty()可以检测一个属性是存在实例中还是原型中,只有存在实例中才会返回true;person.hasOwnProperty("name")

"name" in person 无论name是存在实例中还是原型对象中都会返回true

  //判断对象原型上是否有这个属性
  function hasPrototypeProperty(obj, attr){
    return (attr in obj) && !obj.hasOwnProperty(attr);
  }

在使用for-in 循环时,返回的是能够通过对象访问的,可枚举的属性,包括了实例上的属性和原型上的属性。原型上不可枚举的属性,但是在实例中定义了也可以获取得到,如在实例中定义toString

Object.keys() (es5)

取得对象上所有可枚举的实例属性,返回一个包含所有可枚举的字符串数组

也可以传入一个原型对象,Object.keys(Person.prototype),但不会沿着原型链往上寻找,只返回当前prototype下的属性

Object.getOwnPropertyNames()(ie9+)

枚举所有实例属性,不管是否可枚举

原型的动态性

在原型中查找值的过程是一次搜索,对原型 对象所做的任何修改都能立即从实例上反映出来--即使是先创建了实例后修改原型也照样如此

  var friend = new Person();
  Person.prototype.sayHi = function(){
      
    alert("hi")
  }
  friend.sayHi() //hi

实例和原型之间松散的连接关系,可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反映出来

但是重写整个原型对象就不一样了,调用构造函数时会为实例添加一个指向最初原型的[[prototype]]指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型的联系

实例中的指针仅指向原型对象,而不指向构造函数

  var friend = new Person();
  Person.prototype = {
    sayHi:function(){
      alert("hi")
    }
  }
  friend.sayHi() //error

原型模式的缺点

省略了为构造函数传递初始化参数,所有实例在默认情况下都将取得相同的值

当使用原型属性时会只要在一个实例上修改都会影响到所有的实例,例如在一个实例上修改数组

组合使用构造函数和原型模式

构造函数定义实例属性,原型模式定义方法和属性

动态原型模式

通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型

 function Person(name,age){
  this.name = name;
  this.age = age;
  **if( typeof this.sayName != "funcction"){
     Person.prototype.sayName = function(){
      alert("hi")
    }
  }**
}

只有初次调用构造函数时才会执行将函数添加到原型中

寄生构造函数模式

类似于工厂模式,封装创建对象的代码,然后返回新创建对象,return语句可以重写调用构造函数时返回的值

  function Person(name,age){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function(){
      alert("hi");
    }
  return o;
  }

使用场景:特殊情况下用来为对象创造构造函数。

假设创建一个具有额外方法的特殊数组,由于不能直接修改Array构造函数

function SpecialArray(){
  var arr = new Array();
  arr.push.apply(values,arguments);
  arr.toPipedString = function(){
  return this.join("|");  
}
return arr;

colors = new SpecialArray("green","red");
colors.toPipedString()

缺点:返回的对象和构造函数或者与构造函数的原型属性之间没有什么关系,不能使用instanceof来确定对象类型

稳妥构造函数模式

特点

新创建对象的实例方法不引用this

不使用new操作符调用构造函数

除了返回对象上的属性和方法,没有其他办法访问到构造函数内的数据

function Person(name,age){
  var o = new Object();
  //这里可以定义私有数据
  o.sayName = function(){
    alert(name)
}
return o;

person = Person("green",12);
person.sayName() //只能通过sayName()方法去访问name的值

继承 原型链继承

js以原型链作为实现继承的主要方法

基本思想是利用原型链让一个引用类型继承另一个引用类型的属性和方法

构造函数、原型、实例的关系

每个构造函数都有一个原型对象

原型对象都有一个指向构造函数的方法

实例都包含一个指向原型对象的一个指针

function SuoperType(){
  this.property = true; 
}
SuperType.prototype.getSuperValue = function(){
  return this.property;
}
function SubType(){
  this.subproperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubType = function(){
  return this.subprototype;
}
var instance = new SubType()
instance.getSuperValue();//true

SubType不仅具有作为一个SuperType的实例所拥有的全部属性和方法,而且拥有一个指向SuperType原型的指针

instance指向SubType原型,SubType原型又指向SuperType的原型

确定实例与原型的关系

instanceof操作符 只要用这个操作符来测试实例与原型中出现过的构造函数,结果就会返回true

isPropertyOf() 只要是原型链中出现的原型,都可以说是该原型链所派生的实例的原型,都会返回true

缺点:

原型属性会被所有实例共享,如果在父构造函数中有一个this.colors=[]的数组,子构造函数继承后的实例可以修改这个存在于子构造函数原型对象上的原型属性

在创建子类型的实例时,不能向超类型的构造函数传递参数。实际上是没有办法在不影响所有实例的情况下给超类型的构造函数传递参数

借用构造函数

在子类型构造函数的内部调用超类型构造函数,通过call,apply 改变对象的this指向

function SuoperType(name){
  this.colors = ["red"]; 
  this.name = name;
}

function SubType(name){
  SuperType.call(this,name);
}
SubType.prototype = new SuperType();
var instance = new SubType("1")
instance.colors.push("green") => ["red","green"]

var instance2 = new SubType("2")
instance2.colors.push("black") => ["red","black"]
组合继承

将原型链和借用构造函数的技术组合到一起

使用原型链实现对原型属性和方法的继承

使用借用构造函数实现对实例属性的继承

function SuperType(name){
  this.colors = ["red"]; 
  this.name = name;
}
SuperType.prototype.sayName = function(){
  alert(this.name)
}
function SubType(name,age){
  SuperType.call(this,name);
  this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayName = function(){
  alert(this.name)
}
var instance = new SubType("1",12)
instance.sayName() => 1
instance.colors => ["red"]
var instance2 = new SubType("2",21)
instance2.sayName() => 2
instance2.colors => ["red"]
原型式继承

基于一个对象上,这个对象相当于作为原型,再根据需求对得到的对象加以修改

在没有必要兴师动众创建构造函数,而只想让一个对象与另一个对象保持类似的情况下使用

  function object(o) {
    function F(){}
    F.prototype = o;
    return new F();
  }
  var person = {
    name:"n",
    friends:["a","b"]
  }
  var anotherPerson = object(person);
  anotherPerson.name = "y";  =>y
  anotherPerson.colors.push("c");  => ["a","b","c"]
  var person = object(person);
 person.name = "yy";  =>yy
person.color; =>["a","b","c"]

Object.create() es5新增规范原型式继承

var person = {
 name:"n",
 friends:["a","b"]
}
var anotherPerson = Object.create(person);
anotherPerson.name = "y";  =>y
anotherPerson.colors.push("c");  => ["a","b","c"]
var person =  Object.create(person);
person.name = "yy";  =>yy
person.color; =>["a","b","c"]

寄生式继承

创建一个仅用于封装继承过程的函数,在函数内部以某种方式增强对象

  function object(o) {
    function F(){}
    F.prototype = o;
    return new F();
  }
  function createAnother(original){
    var clone = object(original);
    clone.sayHi = function(){
      alert("hi")
    }
  return clone;
  }
  var person = {
    name:"n",
    friends:["a","b"]
  }
  var anotherPerson = createAnother(person);
  anotherPerson.sayHi(); =>hi
寄生组合式继承

组合继承最大的问题是无论什么情况下都会调用两次超类型构造函数

一次是创建子类型原型时,一次是子函数的内部构造函数

寄生组合继承通过借用构造函数来继承属性,通过原型链混成形式来继承方法。

思路是不必为了指定的子类型原型而调用超类型的构造函数。

使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型

    function inheritPrototype(subType,superType){
      var prototype = object(superType);
      prototype.constructor = subType;
      subType.prototype = prototype;
  }

function SuperType(name){
  this.colors = ["red"]; 
  this.name = name;
}
SuperType.prototype.sayName = function(){
  alert(this.name)
}
function SubType(name,age){
  SuperType.call(this,name);
  this.age = age;
}
SubType.prototype =inheritPrototype(subType,superType) ;
SubType.prototype.sayName = function(){
  alert(this.name)
}

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

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

相关文章

  • 《javascript高级程序设计》第六章 读书笔记 之 javascript继承的6种方法

    摘要:继承的是超类型中构造函数中的属性,如上继承了属性,但没有继承原型中的方法。上述造成的结果是子类型实例中有两组超类型的构造函数中定义的属性,一组在子类型的实例中,一组在子类型实例的原型中。 ECMAScript只支持实现继承,主要依靠原型链来实现。与实现继承对应的是接口继承,由于script中函数没有签名,所以无法实现接口继承。 一、原型链 基本思想:利用原型让一个引用类型继承另一个引用...

    孙吉亮 评论0 收藏0
  • Javascript 设计模式读书笔记(三)——继承

    摘要:的继承方式属于原型式继承,非常灵活。当使用关键字执行类的构造函数时,系统首先创建一个新对象,这个对象会继承自构造函数的原型对象新对象的原型就是构造函数的属性。也就是说,构造函数用来对生成的新对象进行一些处理,使这个新对象具有某些特定的属性。 继承这个东西在Javascript中尤其复杂,我掌握得也不好,找工作面试的时候在这个问题上栽过跟头。Javascript的继承方式属于原型式继承,...

    cangck_X 评论0 收藏0
  • JS高程读书笔记--第六章原型继承

    摘要:高程读书笔记第六章理解对象创建自定义对象的方式有创建一个实例,然后为它添加属性和方法。创建了自定义的构造函数之后,其原型对象默认只会取得属性至于其他方法都是从继承而来的。 JS高程读书笔记--第六章 理解对象 创建自定义对象的方式有创建一个Object实例,然后为它添加属性和方法。还可用创建对象字面量的方式 属性类型 ECMAScript在定义只有内部采用的特性时,描述了属性的各种特征...

    EasonTyler 评论0 收藏0
  • 读书笔记(02) - 可维护性 - JavaScript高级程序设计

    摘要:解耦优势代码复用,单元测试。常用比较误区可同时判断,可用来判断对象属性是否存在。使用作判断无法进行充分的类型检查。文件中应用常量参考文档高级程序设计作者以乐之名本文原创,有不当的地方欢迎指出。 showImg(https://segmentfault.com/img/bVburXw?w=500&h=400); 编写可维护性代码 可维护的代码遵循原则: 可理解性 (方便他人理解) 直观...

    k00baa 评论0 收藏0
  • 读书笔记-1【javascript语言精粹】继承

    摘要:使用构造器有个严重的危害,如果在调用构造器函数的时候忘记使用前缀,不仅不会绑定到新对象,还会污染全局变量原型模式原型模式中,我们采用对象来继承。 构造器调用模式 当一个函数对象被创建时,Function构造器会运行类似这样的代码: this.prototype = {constructor: this} new一个函数事会发生: Function.method(new, functio...

    malakashi 评论0 收藏0

发表评论

0条评论

LuDongWei

|高级讲师

TA的文章

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