05-创建对象

2017-05-22  本文已影响0人  LeoCong

创建对象

对象是无序属性的集合,其属性可以包含基本值,对象或者函数,即由若干个“键值对”(key-value)构成。
创建对象有3种方法:1.对象字面量;2.构造函数;3.对象继承。

    function createPerson(name, age) {
        // 1.声明一个中间对象,该对象就是工厂模式的模子
        var o = new Object();
        // 2.添加属性和方法
        o.name = name;
        o.age = age;
        o.getName = function() {
            return this.name;
        }
        // 3.返回中间对象
        return o;
    }
    // 创建对象实例
    var p1 = createPerson('Leo', 26);
    var p2 = createPerson('Tom', 20);
    console.log(p1 instanceof Object); // true
    console.log(p1 instanceof createPerson); // false

工厂模式缺点:无法识别对象实例的类型,因为构造函数都指向Object。

var A = function () {
    this.x = 1;
    return {x: 2};
}
console.log( (new A()).x ); // 2

new 命令简化的内部流程:

    // 创建构造函数
    function Person(name, age) {
        this.name = name;
        this.age = age;
        this.getName = function () {
            return this.name;
        }
    }
    Person.prototype.getAge = function () {
        return this.age;
    }
    // 模拟new命令
    function New(func) {
        // 将类数组arguments对象转换为数组
        var args = [].slice.call(arguments);
        // 取出构造函数
        var func = args.shift();
        // 创建新对象,该对象的原型指向继承构造函数的原型,继承构造函数的属性和方法
        var obj = Object.create(func.prototype);
        // result为构造函数执行的结果,通过apply将构造函数内的this指向修改为实例对象
        var result = func.apply(obj, args);
        // 当构造函数指定了返回的对象时,New的执行结果就返回该对象,否则返回实例对象
        return (typeof result === 'object' && result != null) ? result : obj;
    }
    var p1 = New(Person, 'Tom', 20);
    console.log(p1.getName()); // Tom
    console.log(p1.getAge()); // 20
    console.log(p1 instanceof Person); // true
// 其他的一些特殊处理,将var p1=New(Person,'Tom',20)等效于var p1 = new Person('Tom',20);
    function Person(name, age) {
        this.name = name;
        this.age = age;
        this.getName = function () {
            console.log(this.name);
        };
    }
    var p1 = new Person('Leo', 26);
    var p2 = new Person('Tom', 20);
    console.log(p1.getName === p2.getName); // false

构造函数模式的缺点:
每次创建实例都要创建一遍方法,这既没有必要,又浪费系统资源。

    // 私有属性和方法
    function Person(name, age) {
        this.name = name;
        this.age = age;
        this.getAge = function () {
            console.log(this.age);
        }
    }
    // 共有属性和方法
    Person.prototype = {
        constructor: Person,
        getName: function () {
            console.log(this.name);
        }
    };
    var p1 = new Person('Tom', 10);
    var p2 = new Person('Leo', 26);
    console.log(p1.getName === p2.getName); // true

组合模式的缺点:构造函数和原型没有封装在一起。

    function Person(name, age) {
        this.name = name;
        this.age = age;
        // 第一调用构造函数初始化原型
        if (typeof this.getName !== "function") {
            Person.prototype.getName = function () {
                console.log(this.name);
            }
        }
    }
    var person1 = new Person('Leo', 26); // 初次调用构造函数时,初始化原型
    var person2 = new Person('Tom', 20); // 原型已初始化
    person1.getName(); // Leo
    person2.getName(); // Tom

注意:使用动态原型模式时,不能使用对象字面量重写原型,这样做会切断所有实例对象与新原型的联系。
重写原型对象会切断现有原型与任何之前已存在的实例对象直接的联系。

    function Person(name, age) {
        this.name = name;
        this.age = age;
        if (typeof this.getName !== "function") {
            Person.prototype = {
                constructor: Person,
                getName: function () {
                    console.log(this.name);
                }
            };
        }
    }
    var p1 = new Person('Leo', 26);
    var p2 = new Person('Tom', 20);
    p2.getName(); // Tom
    p1.getName(); // 报错,p1.getName不存在
    console.log(Person.prototype === p1.__proto__); // false
    console.log(Person.prototype === p2.__proto__); // true
    console.log(p1.__proto__ === p2.__proto__); // false
// 第一次调用构造函数时,重写原型,添加原型方法getName
// 而此时实例p1的__proto__仍然指向以前的原型对,此原型没有getName方法,报错
// 第二次调用构造函数的时候,实例p2已继承原型方法getName
解决办法:
    function Person(name, age) {
        this.name = name;
        this.age = age;
        if (typeof this.getName !== "function") {
            Person.prototype = {
                constructor: Person,
                getName: function () {
                    console.log(this.name);
                }
            };
            return new Person(name, age); //返回一个实例
        }
    }
    var p1 = new Person('Leo', 26); // 第一次调用时执行if语句返回一个具有新原型的对象实例
    var p2 = new Person('Tom', 20);
    p2.getName(); // Tom
    p1.getName(); // Leo
    function Person(name, age) {
        var o = new Object();
        o.name = name;
        o.age = age;
        o.getName = function () {
            console.log(this.name);
        };
        return o;
    }
    var p1 = new Person('Leo', 26); // 使用new操作符
    console.log(p1 instanceof Person) // false 该模式不能识别构造函数
    console.log(p1 instanceof Object)  // true

这个模式可以在特殊情况下来为对象创建构造函数。
假如我们需要创建一个具有额外方法的特殊数组,由于不能直接修改Array构造函数,可以使用该模式。

    function SpecialArray() {
        // 创建新数组
        var arr = new Array();
        // 给数组添加值
        arr.push.apply(arr, arguments);
        // 给数组添加额外的方法
        arr.toPipedString = function () {
            return this.join('|');
        }
        // 返回该数组
        return arr;
    }
    var prople = new SpecialArray('Leo', 'Tom', 'Bob');
    console.log(prople.toPipedString()); // Leo|Tom|Bob
    function person(name, age){
        var o = new Object();
        o.getName = function(){
            console.log(name);
        };
        o.getAge = function(){
            console.log(age);
        };
        return o;
    }
    var p1 = person('Leo', 26);
    p1.getName(); // Leo
    p1.getAge(); // 26
    console.log(p1.name); // undefined,只能通过getName()方法访问name值

《JavaScript高级程序设计》
《JavaScript 标准参考教程》

上一篇下一篇

猜你喜欢

热点阅读