05-创建对象

创建对象

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

  • Object对象
    JavaScript原生提供一个Object对象,所有其他对象都继承自这个对象。
    Object本身也是一个构造函数,可以直接通过它来生成新对象。
    通过new Object()的写法创建新对象,与字面量的写法o = {}是等价的。

  • 工厂模式
    工厂模式就是我们提供一个模子,然后通过这个模子复制出我们需要的对象。

    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。

  • 构造函数
    JavaScript中使用构造函数作为对象的模板,专门用来生成对象。
    构造函数特点:
    1. 生成对象的时候,必需用new命令,调用构造函数。
    2. 函数体内部使用了this关键字,代表了所要生成的对象实例。
      new 命令
      new命令的作用就是执行构造函数,并返回一个实例对象。
      new命令本身就可以指向构造函数,所有new后面的构造函数可以带括号,也可以不带。
      如果没有使用new命令,构造函数就变成了普通函数,并不会生成实例对象。
      new 命令的原理
    3. 创建一个新对象,作为将要返回的实例对象。
    4. 将这个新对象的原型,指向构造函数的prototype属性。
    5. 执行构造函数,将构造函数内部的this,指向这个新对象。
    6. 返回新对象。
      在构造函数内部,this指的是这个新建的空对象,所有针对this的操作,都会发生在这个空对象上。
      如果构造函数内部有return语句,而且后面跟一个对象,new命令会返回这个对象,否则返回this对象。
      如果普通函数(内部没有this关键字)使用new命令,则会返回一个空对象。
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

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

  • 原型
    每个函数都有一个prototype属性,该属性指向一个对象,这个对象就是原型对象。
    构造函数的prototype属性指向实例对象的原型对象。
    每个原型对象都有一个constructor属性,该属性默认指回原型对象的构造函数。
    每个实例对象都可以通过__proto__访问该实例对象的原型对象。
    原型对象的作用就是定义所有实例对象共享的属性和方法。
    实例对象可以看作从原型对象衍生出来的子对象,原型对象上的所有属性和方法,都能被实例对象共享。
    实例对象本身没有某个属性或方法时,会到原型对象去寻找该属性或方法。
    原型对象上的变动会立刻体现在所有实例对象上,原型上的属性和方法被所有实例共享。

  • 组合模式
    组合使用构造函数和原型:
    构造函数中通过this绑定的属性与方法称为私有变量与方法,它们被当前被某一个实例对象所独有。
    原型中的属性和方法称之为共有属性与方法,它们可以被所有的实例对象共享。

    // 私有属性和方法
    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
  • 寄生构造函数模式
    除了使用new操作符,并把封装的函数称为构造函数之外,这个模式和工厂模式一样。
    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
  • 稳妥构造函数模式
    稳妥对象,是指没有公共属性,而且其方法也不引用this的对象。
    与寄生构造函数模式有两点不同:
    新创建的实例方法不引用this;不使用new操作符调用构造函数。
    稳妥构造函数模式也跟工厂模式一样,无法识别对象所属类型。
    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 标准参考教程》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容