JavaScript高级程序设计(第三版)6章

Menu

  • 第6章 面向对象的程序设计
    • 6.1 理解对象
      • 6.1.1 属性类型
      • 6.1.2 定义多个属性
      • 6.1.3 读取属性的特性
    • 6.2 创建对象
      • 6.2.1 工厂模式
      • 6.2.2 构造函数模式
      • 6.2.3 原型模式
      • 6.2.4 组合使用构造函数模式和原型模式
      • 6.2.5 动态原型模式
      • 6.2.6 寄生构造函数模式
      • 6.2.7 稳妥构造函数模式
    • 6.3 继承 》笔记写到这里
      • 6.3.1 原型链
      • 6.3.2 借用构造函数
      • 6.3.3 组合继承
      • 6.3.4 原型式继承
      • 6.3.5 寄生式继承
      • 6.3.6 寄生组合式继承

第6章 面向对象的程序设计

6.1 理解对象

ECMA-262 把对象定义为:“无序属性的集合,其属性可以包含基本值、对象或者函数。” 严格来讲,这就相当于说对象是一组没有特定顺序的值。

  • 6.1.1 属性类型

特性是内部值,放在两对儿方括号中,例如[[Enumerable]];
这些特性是为了实现 JavaScript 引擎用的,因此在 JavaScript 中不能直接访问它们。

    1. 数据属性
    • 数据属性包含一个数据值的位置, 在这个位置可以读取和写入值。
    • 数据属性有 4 个描述其行为的特性:
      • [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。
      • [[Enumerable]]:表示能否通过 for-in 循环返回属性。
      • [[Writable]]:表示能否修改属性的值。
      • [[Value]]:包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为 undefined。
      • 对于直接在对象上定义的属性,它们的[[Configurable]]、[[Enumerable]]和[[Writable]]特性都被设置为 true,而[[Value]]特性被设置为指定的值。
  • Object.defineProperty()方法:可以修改属性默认的特性;
    • 接受3个参数:属性所在的对象、属性的名字和一个描述符对象(在描述符对象内修改4个特性);
    var person = {};
    Object.defineProperty(person, "name", {
        writable: false,
        value: "smm"
    });
    document.write(person.name);//smm
    person.name = "xhn";
    document.write(person.name);//smm
  • [[Configurable]] 特性一旦设置为不可配置(false), 则再也不能设置为可配置(true);此时,再调用 Object.defineProperty()方法修改除 writable 之外的特性,都会导致错误:
    var person = {};
    Object.defineProperty(person, "age", {
        configurable: false,
        value:"12"
    });

    Object.defineProperty(person, "age", {
        configurable: true,
        value:"12"
    });
    //抛出异常:can't redefine non-configurable property "age"
  • Configurable特性设置为false时, 如果writable也时false, 再调用修改writable为true也会抛出异常。 但是Configurable特性设置为false时, 如果writable设置的是true, 再调用修改writable为false则不会抛出异常;(书里有出入)。
    // 以下不会异常
    var person = {};
    Object.defineProperty(person, "age", {
        configurable: false,
        writable:true
    });
    
    Object.defineProperty(person, "age", {
        writable:false
    });

    // 以下会异常
    var person = {};
    Object.defineProperty(person, "age", {
        configurable: false,
        writable:false
    });
    
    Object.defineProperty(person, "age", {
        writable:true
    });
  • 在调用 Object.defineProperty()方法时,如果不指定,configurable、 enumerable 和writable 特性的默认值都是 false。多数情况下,可能都没有必要利用 Object.defineProperty()方法提供的这些高级功能。
    1. 访问器属性
    • 访问器属性不包含数据值;它们包含一对儿 getter 和 setter 函数;
    • 在读取访问器属性时,会调用 getter 函数,这个函数负责返回有效的值;在写入访问器属性时,会调用setter 函数并传入新值,这个函数负责决定如何处理数据。
    • 访问器属性 4 个特性:
      • [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。对于直接在对象上定义的属性,这个特性的默认值为true。
      • [[Enumerable]]:表示能否通过 for-in 循环返回属性。对于直接在对象上定义的属性,这个特性的默认值为 true。
      • [[Get]]:在读取属性时调用的函数。默认值为 undefined。
      • [[Set]]:在写入属性时调用的函数。默认值为 undefined。
    • 访问器属性不能直接定义,必须使用 Object.defineProperty()来定义;
    var book = {
        _year: 2004,
        edition: 1
    };
    Object.defineProperty(book, "year", {
        get: function(){
            return this._year;      
        },                        // 通过get函数为属性“year”返回“_year”的值;
        set: function(newValue){
            if (newValue > 2004) {
                this._year = newValue;
                this.edition += newValue - 2004;
            }                    // 通过set函数设置如果year赋了新值, 改变_year和edition两个属性值。
        }
    });
    book.year = 2005;
    document.write(book.edition); //2
  • 只指定 getter 意味着属性是不能写,尝试写入属性会被忽略。
    var book = {
        _year: 2004,
        edition: 1
    };
    Object.defineProperty(book, "year", {
        get: function(){
            return this._year;
        },
        // set: function(newValue){
        //     if (newValue > 2004) {
        //         this._year = newValue;
        //         this.edition += newValue - 2004;
        //     }
        // }
    });
    book.year = 2005; //被忽略
    document.write(book.edition); //1
  • 只指定 setter 函数的属性也不能读,会返回 undefined;
  var book = {
        _year: 2004,
        edition: 1
    };
    Object.defineProperty(book, "year", {
        // get: function(){
        //     return this._year;
        // },
        set: function(newValue){
            if (newValue > 2004) {
                this._year = newValue;
                this.edition += newValue - 2004;
            }
        }
    });
    book.year = 2005;
    document.write(book.year); //undefined
    document.write(book.edition); //2
  • 6.1.2 定义多个属性
    • Object.defineProperties()方法
      • 利用这个方法可以通过描述符一次定义多个属性
      • 这个方法接收两个对象参数:第一个对象是要添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应。
    // 与上一节中定义的对象相同。唯一的区别是这里的属性都是在同一时间创建的。
      var book = {};
    Object.defineProperties(book, {
        _year: {
            value: 2004
        },
        edition: {
            value: 1
        },
        year: {
            get: function(){
                return this._year;
            },
            set: function(newValue){
                if (newValue > 2004) {
                    this._year = newValue;
                    this.edition += newValue - 2004;
                }
            }
        }
    });
    re = []
    for (i in book){
        re.push(i, ">", book[i]);
    }
    document.write(re);//拿不到东西????
  • 6.1.3 读取属性的特性
    • Object.getOwnPropertyDescriptor()方法
      • 可以取得给定属性的描述符,返回值是一个对象;
    var descriptor = Object.getOwnPropertyDescriptor(book, "_year");
    document.write(descriptor.value); //2004
    document.write(descriptor.configurable); //false
    alert(typeof descriptor.get); //"undefined"
    
    var descriptor = Object.getOwnPropertyDescriptor(book, "year");
    document.write(descriptor.value); //undefined
    document.write(descriptor.enumerable); //false
    document.write(typeof descriptor.get); //"function"
6.2 创建对象
  • 6.2.2 构造函数模式
  • 创建构造函数
 // 构造函数首字母大写,相当于其它语言的Class
    function Person(name, age, job){    
        this.name = name;
        this.age = age;
        this.job = job;
// 如果不写prototype, 那么所有new出来的实例对向都会new一个新的sayName实例对象;加上prototype可以让所有Person的实例对象共享一个方法;
        Person.prototype.sayName = function(){          // 定义方法
            alert(this.name);
        };
    }
    // 创建构造函数实例 
    var person1 = new Person("Nicholas", 29, "Software Engineer");   
  • 在默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。就拿前面的例子来说,Person.prototype. constructor 指向 Person。而通过这个构造函数,我们还可继续为原型对象添加其他属性和方法。


    图片.png
  • 原型与 in 操作符
    • in操作符能判断该对象是否有指定的属性;
    • 与hasOwnProperty("property_name") 相同作用,hasOwnProperty()只在属性存在于实例中时才返回 true,如实例里没有,原型对象里有,还是返回false;
    • example:
    // true  会先找person1里的name属性,如person1里没有,就会区原型对象里找name属性,
    // 有的话返回true,没有返回false
    document.write("name" in person1)  
    person1.hasOwnProperty("name")  // true
  • Object.keys()
    • 返回对象里可迭代的属性;
    function Person(){
        this.name = "Nicholas";
        this.age = 29;
        Person.prototype.job = "Software Engineer";
        Person.prototype.sayName = function(){
            alert(this.name);}
    };
    p = new Person()
    // job,sayName 找原型对象Own的可迭代的属性
    document.write(Object.keys(Person.prototype));
    // name,age  找实例对象自己Own的可迭代属性。
    document.write(Object.keys(p));

    Object.getOwnPropertyNames(Object) // return all properties
  • 原型对象属性的封装语法:
    • 为更好地封装原型的功能,更常见的做法是用一个包含所有属性和方法的
      对象字面量来重写整个原型对象

    Person.prototype = {

        name : "Nicholas",
        age : 29,
        job: "Software Engineer",
        sayName : function () {
            alert(this.name);
        }
    };
  • 为原生对象的原型添加方法
    String.prototype.startsWith = function (text) {
        return this.indexOf(text) == 0;
    };
    var msg = "Hello world!";
    alert(msg.startsWith("Hello")); //true
  • 6.2.7 稳妥构造函数模式
    • 稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:一是新创建对象的实例方法不引用 this;二是不使用 new 操作符调用构造函数。按照稳妥构造函数的要求,可以将前面的 Person 构造函数重写如下
    function Person (age) {
        var o = new Object();
        var age = age;
        o.sayAge = function(){
            return age;
        };
        return o;
    }
    var friend = Person(17);
    document.write(friend.age, "
"); // undefined // 只有通过方法才能get到内部的值 document.write(friend.sayAge()); // 17
6.3 继承
  • 6.3.1 原型链
    • 实例的 _proto_ ----> 构造函数的原型对象 ----> Object的原型对象 ----> null
      实例的 __proto__ ----> 构造函数的原型对象 ----> Object的原型对象 ----> null
    • 构造函数和原型对象和 实例对象之间的关系
function F(){
        document.write(F.prototype.constructor===F)
    }
    f()// true

    // 构造函数function(.prototype)-> 原型对象(.constructor)->构造函数
    // 构造函数(new)-> 实例object (.__proto__)-> 原型对象(.constructor)->构造函数
构造函数和原型对象和 实例对象之间的关系
  • 原型链分析


    原型链分析图
    function F1(){
        this.name = "nameF1";
    }
    F1.prototype.getf1name = function(){
        return this.name;
    }
    function F2() {
        this.name = "nameF2";
    }
    F2.prototype = new F1();
    F2.prototype.getf2name = function(){
        return this.name;
    }
    F2.prototype.getf1name = function(){
        return "newname";
    }

    var f2instance = new F2();
    document.write(f2instance.getf1name());// newname; 重新定义继承自祖辈的方法, 会覆盖原方法

    var f1instance = new F1();
    document.write(f1instance.getf1name()); //nameF1;重新定义继承自祖辈的方法,不会改变祖辈自己的调用
  • 通过原型链实现继承时,不能使用对象字面量创建原型方法。因为这样做就会重写原型链;

  • 原型链的问题1:.引用类型值的原型属性会被所有实例共享;

    function F1(){
        this.colors = ["red", "blue", "green"];
    }
    function F2(){}
    F2.prototype = new F1();
    var instance1 = new F2();
    instance1.colors.push("black");
    document.write(instance1.colors); //"red,blue,green,black"
    var instance2 = new F2();
    document.write(instance2.colors); //"red,blue,green,black"
  • 6.3.2 解决办法:借用构造函数;
    function F1(){
        this.colors = ["red", "blue", "green"];
    }
    function F2(){
        F1.call(this);
    }
    var instance1 = new F2();
    instance1.colors.push("black");
    document.write(instance1.colors); //"red,blue,green,black"
    var instance2 = new F2();
    document.write(instance2.colors); //"red,blue,green"
  • 原型链的问题2:创建子类型的实例时,不能向超类型的构造函数中传递参数。

  • 6.3.3 组合继承:最常用的继承方式

    • 将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。
    • 使用原型链实现对原型属性和方
      法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数
      复用,又能够保证每个实例都有它自己的属性。
  • 6.3.4 原型式继承:Object.create(o, [描述符]), 传入一个要copy的类型, 返回一个指向这个类型的其它类型的实例, 描述符同Object.defineProperties()的第二个参数。
    -缺点:同原型继承, 共享引用类型值

    var person = {
        name: "Nicholas",
        friends: ["Shelby", "Court", "Van"]
    };
    var anotherPerson = Object.create(person, {
        name: {
            value: "Greg"
        }
    });
    alert(anotherPerson.name); //"Greg"

page180


  • ;

你可能感兴趣的:(JavaScript高级程序设计(第三版)6章)