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

创建对象的设计模式

  • 工厂模式

    • 抽象了创建具体对象的过程,用函数封装以特定接口创建对象的细节
    • 解决了创建多个相似对象的问题,没有解决对象识别的问题
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;
  1. 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;
  1. 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)
}
时间: 2024-10-07 10:16:11

读书笔记--对象、实例、原型、继承的相关文章

Python笔记(七):字典、类、属性、对象实例、继承

(一)  简单说明 字典是Python的内置数据结构,将数据与键关联(例如:姓名:张三,姓名是键,张三就是数据).例如:下面这个就是一个字典 {'姓名': '张三', '出生日期': '2899-08-12', '成绩': ['3.21', '3.10', '3.01']} 创建字典.添加数据.访问字典数据的方式如下: d = {}  #直接用{}创建字典 f = dict() #通过工厂函数dict()创建字典 #通过下面的方式添加数据 d['姓名'] = '张三' d['出生日期'] = '

深度探索c++对象模型读书笔记:Data语意学-继承与Data member中内存对齐问题

书中在继承之后内存对齐问题上说道下面代码: 1 #include <bits/stdc++.h> 2 using namespace std; 3 class A 4 { 5 private: 6 int val; 7 char bit1; 8 }; 9 class B : public A 10 { 11 private: 12 char bit2; 13 }; 14 class C : public B 15 { 16 private: 17 char bit3; 18 }; 19 int

葵花宝典读书笔记-对象

对象就是一个具体的类,就是一个类的实现. 对象是客观存在的,世界万物皆对象.人类为了归纳,总结,而把具有相似性的对象归类到一个类中.真正存在于软件运行过程中的是对象,而不是类. 软件对象产生的过程: 现实对象->现实类->软件类->软件对象 "软件类来源于现实类,但高于现实类"这句话的意思是.软件类不一定在现实中就存在对应的现实类.大多数软件类抽象于现实类,但他们之间并非一一对应,一个现实类可能对应多个软件来.例如:ATM是一个现实中的类,转化成软件类后就有"

MDN——javascript——入门——第三章对象——对象.构造函数.原型链.继承——知识点总结

对象Object 由属性property(变量).方法method(函数)组成 var objectName = { member1Name : member1Value, member2Name : member2Value, member3Name : member3Value } member(成员)的值是任意的, 一个如上所示的对象被称之为对象的字面量(literal)——手动的写出对象的内容来创建一个对象.不同于从类实例化一个对象,我们会在后面学习这种方式. 访问对象成员 1.点表示法

彻底理解Javascript原型继承

彻底理解Javascript原型继承 之前写过一篇Javascript继承主题的文章,这篇文章作为一篇读书笔记,分析的不够深入. 本文试图进一步思考,争取彻底理解Javascript继承原理 实例成员与原型成员 举一个<高性能Javascript>书中例子 var book={ title :"High Performance JavaScript", publisher:"Yahoo!Press" }; alert(book.toString());/

Node.js的原型继承函数util.inherits

util.inherits(constructor, superConstructor)是一个实现对象间原型继承 的函数.JavaScript 的面向对象特性是基于原型的,与常见的基于类的不同.JavaScript 没有 提供对象继承的语言级别特性,而是通过原型复制来实现的,具体细节我们在附录A中讨论, 在这里我们只介绍 util.inherits 的用法,示例如下: var util = require('util'); function Base() { this.name = 'base'

Node.js的原型继承函数 util.inherits

util.inherits(constructor, superConstructor)是一个实现对象间原型继承 的函数.JavaScript 的面向对象特性是基于原型的,与常见的基于类的不同.JavaScript 没有 提供对象继承的语言级别特性,而是通过原型复制来实现的,具体细节我们在附录A中讨论, 在这里我们只介绍 util.inherits 的用法,示例如下: var util = require('util'); function Base() { this.name = 'base'

node.js javascript理解原型继承

util.inherits util.inherits(constructor, superConstructor)是一个实现对象间原型继承的函数. JavaScript 的面向对象特性是基于原型的,与常见的基于类的不同.JavaScript 没有提供对象继承的语言级别特性,而是通过原型复制来实现的 var util = require('util'); function Base() { this.name = 'base'; this.base = 1991; this.sayHello =

javascript原型继承圣杯模式

javascript纯面向对象开发需要使用到的一个模式,来对对象之间原型继承做中间层代理避免重复继承与代码杂乱 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"