logo头像

孙宇

javascript 面向对象:2

本文于973天之前发表,文中内容可能已经过时。

  1. 为什么需要原型
    构造器创建对象的时候, 实际上会有成员重复
    如果使用 构造器 this.方法名 = function …. 方式创建对象. 那么每一个对象
    对应的方法就会重复.

    解决办法就是让这个方法( 函数 )共享
    -> 将函数写到外面, 那么 Person 在初始化对象的时候就不会再创建一个函数了.

    只需要将 外面的函数引用 交给对象即可.
    
    缺点: 一个对象可能有 n 多方法. 如果将所有的东西 都放到外面, 与其他库
        冲突的几率就会变大. 所以不宜采取该方法.
    

    -> 将所有的方法( 函数 )都绑定到一个对象中.

-> js 原生就支持解决该问题的办法
    每一个函数都有 一个属性 prototype
    该属性指向一对象. 每一个函数的该对象都是存在. 
    (重点) 每一个由该函数作为构造器创建的对象, 都会默认连接到该对象上.
        如果访问对象的方法, 而对象中没有定义, 就会在这个 构造函数.prototype
        表示的对象中去找.

-> prototype 就是原型之意
  1. 针对构造函数而言, 原型就是 构造函数的 prototype 属性, 常常将其称为 原型属性.
    针对实例对象而言, 原型就是 实例对象的 原型对象.

    例:

    function Person () {}    // 有了构造函数, 和 原型
    var p = new Person();     // 有了实例
    
  2. 一般如何使用原型对象
    -> 简单的说就是将共享的方法放到原型中, 而独有数据与行为放在当前对象里
    -> 例:

    Person( name, age, gender, sayHello, eat, run )
    

    -> 直接给原型对象添加成员
    -> 直接替换原型对象( 注意: 手动的添加 constructor 属性, 表示对应的构造函数 )

  3. proto
    和 prototype 是否存在关系

    早期浏览器是不支持 proto
    火狐率先使用该属性, 但是是非标准
    基本现在的新浏览器都支持该属性

    -> 访问

    使用构造函数, 就使用 prototype 属性访问原型
    使用实例对象, 就使用 非标准的 __proto__ 属性访问原型
    
  4. 继承
    -> 什么是继承

    自己没有, 别人有, 拿过来自己用, 就好像自己的一样.
    

    -> 原型与实例对象

    在 js 中, 方法定义在原型对象中, 而属性定义在实例对象中
    调用方法的时候, 实例对象本身是没有该成员的, 但是依旧可以调用
    该方法, 好像这个方法就是该实例对象的一样. 因此, 我们称该实例对象
    继承自 原型对象
    

    -> 任何一个实例, 都是继承自其原型对象的. 即原型式继承.

  5. 为什么需要继承
    -> 编程的发展

    复用( 重复使用 )
    
    div 标签对象    nodeName, nodeType, nodeName, ...
                    appendChild, insertBefore, getElementsByTagName, ...
    
    a 标签对象
    
    baseElement
    

    -> js 运行效率

    共享特性
    复用
    
  6. 传统的编程语言的面向对象操作
    -> 对象: 是具有方法和属性的逻辑单元

    在 js 函数是一个特殊的数据类型( 函数在 js 是一等公民 )
    js 对象就是键值对, 值如果是数据, 那么键值就构成属性
    如果值是函数, 那么就构成方法.
    

    -> 创建方式

    类 -> 实例化 -> 对象( 实例 )
    
    class Person {
        public string name;
        public int age;
        public string gender;
    
        public void sayHello () {
            // ...
        }
    }
    // 类, 用来描述对象的结构, 它就是一个模板
    
    Person p = new Person(); // 利用模板创建对象
    
    // 访问属性
    p.name
    // 调用方法
    p.sayHello();
    
    传统的面向对象编程语言, 重点需要一个"模板", 即 类( class )
    

    -> 传统的继承

    传统的继承是模板的继承.
    
    class Student : Person {
    }
    
    Student s = new Student();
    // 注意此时 s 就可以调用 sayHello 方法
    // 可以使用 name, age, 和 gender 属性了
    
  7. 相关概念
    类 class 模板 构造函数, 类名就是构造函数名
    子类 subclass 派生的模板 原型设置为指定对象的构造函数
    实例 instance 某个类的对象 …
    实例成员(实例方法, 实例属性)
    静态成员

    静态方法            ...                        直接绑定在函数上的方法
    静态属性            ...                        直接绑定在函数上的属性
    
// js 代码
function Person () {
    this.name = '黄帝';
    this.age = 0;
    this.gender = '男';
}

// 不叫子类, 只是一个 Student 类
function Student() {
}

// 继承派生 成为子类
Student.prototype = new Person();

// 即完成派生
var s = new Student();
s.name // OK
  1. 属性访问原则( 重点 )
    1) 对象在调用方法或访问属性的时候, 首先在当前对象中查询. 如果有该成员使用并停止查找.
    2) 如果没有该成员就在其原型对象中查找. 如果有该成员即使用, 并停止查找.
    3) 如果还没有就到 该对象的 原型对象 的 原型对象中查找.

    4) 最后会查到 Object.prototype 上. 如果还没有即 返回 undefined.

  2. 如果修改原型对象中的属性值会怎样
    给当前对象的原型提供的属性赋值, 实际上是给当前对象添加了该属性的新成员
    并不会修改运行对象中的成员.

  3. 混入( mix )

    var o1 = { name: ‘张三’ };

    var o2 = { age: 19 };

    o2.name = o1.name
    利用这个简单的赋值, 就可以将一个对象中的成员加到另一个对象中
    执行混入操作, 如果o2里面有name值,则将会被覆盖。
    混入使得 o2 具有了 age 和 o1 中的 name. 即将 o1 混入到 o2 中

    混入也是一种继承的方式
    实例

  4. 如何使用代码实现混入

    // 考虑需要执行, 写函数即可
    // 由于将 一个 对象混入到 另一个对象. 所以有两个参数
    function mix ( obj, obj1 ) {

    // 如何获得 obj1 中的所有成员?
    for ( var  k in obj1 ) {
        obj[ k ] = obj1[ k ];
    }
    

    }

  5. 原型式继承
    -> 写一个构造函数, 如果需要将其实例继承自某个特定的对象 o. 那么

    只需要设置该构造函数的 prototype 属性为 o 即可
    
    function Person {}
    
    var o = { ... }
    
    Person.prototype = o;    // 继承
    
  6. 混合式继承
    -> 混合就是将多个对象的各个功能混合到一起, 加到构造函数的原型对象上.

    那么该构造函数创建的实例 就继承自多个对象了.
    
  1. 对象 -> 父对象 -> 父对象的父对象 -> … -> 原对象 -> ….
    链式结构( 原型链 )

    由于链越深, 属性搜索越需要性能. 所以一般少用深层次的链式结构继承
    一般使用时, 链结构只有 3 级. 一般都是使用混入, 在原型上加成员

  1. ES5 中 的 Object.create
    继承的最经典结构

    function Animal() {}
    
    function Person() {}
    
    Person.prototype = new Animal();
    

    语法:

    新对象 Object.create( 原对象 );
    

    例如

    var o ={name:"sy",age:20,gender:"男"}
     var obj = Object.create(o);
    实现继承的快速方法
    

如果在低版本的浏览器中要使用该方法 ,先判断有没有,如果没有在加上。
if(!Object.create){
Object.create = function(o) {
function F(){}
f.prototype = o;
return new F();
};
}
这个写法已经过时了
现在使用
function createWithObject ( obj ) {
if ( Object.create ) {
return Object.create( obj );
} else {
function F() {}
F.prototype = obj;
return new F();
}
}

实际应用

// jQuery
// Sizzle 引擎 select 方法
// 在这个方法, 首先判断系统是否具有 querySelectAll 方法. 如果有直接使用
// 如果没有 再自己实现