搜索

首页  >  问答  >  正文

javascript - 关于js中组合使用构造函数模式和原型模式的写法

高级程序设计里面的写法是下面这样的

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.lessons = ['Math','Physics'];
}
Person.prototype = {
    constructor: Person,
    getName: function(){
        return this.name;
    }
}

那么我像下面这样写是不是一样的,区别只在于他们的constructor不一样?

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.lessons = ['Math','Physics'];
    Person.prototype.getName = function(){
        return this.name;
    }
}
曾经蜡笔没有小新曾经蜡笔没有小新2780 天前659

全部回复(5)我来回复

  • PHP中文网

    PHP中文网2017-05-19 10:35:10

    prototype定义(的位置)写在构造函数内或外的区别 ?

    第二种写法每次创建实例都会执行一遍对prototype的操作! 关键是这个操作很没意义,这个方法对每个实例都是相同的。
    第一种方法中, 当prototype在构造函数外面书写时,可以从形式上和内存分配中解决重复定义或分配内存的问题。
    对应在内存中,第一种写法,无论你创建了多少实例,每个实例占据空间只是name, age,job和lessons。getName在内存中只有一份,所有实例共用; 第二种写法,每个新创建的实例会分配一块额外的空间(栈)来执行prototype的定义。

    prototype在第一种方法中赋值的方式和第二种方法有何区别?

    区别很大的,一个function类定义好,它默认的 constructor属性就是自己, 它的实例在访问constructor属性时会返回这个值。

    function Person() {};
    console.log(Person.prototype.constructor);  // Person
    
    var p = new Person();
    console.log(p.constructor);   // Person    表示p的构造函数是Person类

    方法1中为什么要定义constructor? 因为它给prototype重新赋值了,如果你不定义constructor(Person.prototype = {getName: function() {}}),那么上例中p.constructor返回值将是 Object, 即p的构造函数是Object,显然与事实不符。

    方法1更明智的做法是不要重新给prototype赋值,只为prototype添加我们需要的属性getName, 改为 Person.prototype.getName = function() {return this.name;},也就是第二种方法里的定义方法,这么写就不会覆盖prototype的默认属性。

    回复
    0
  • 習慣沉默

    習慣沉默2017-05-19 10:35:10

    雷雷

    回复
    0
  • PHPz

    PHPz2017-05-19 10:35:10

    前一种写法重写了prototype,而你的写法只是在prototype里增加了一个方法而已,两个是不同的方式

    回复
    0
  • 世界只因有你

    世界只因有你2017-05-19 10:35:10

    按照你的写法会在每一次实例化过程中重新再分配存储空间给实例,而原型模式的意义之一在于所有实例都可以共享原型上的属性和方法,虽然单独这么做有缺陷。第二点就是我还是倾向于给原型对象对象字面量的写法,个人认为一个是比较直观,第二是有利于维护。如下:

        Person.prototype = {
            constructor: Person,
            getName: function(){
                return this.name;
            }
        }

    一定要写constructor属性,不然会发生指向的错误,此时是重写了原型对象,如果不指明这个属性就无法起到原型链应有的作用。

    回复
    0
  • 为情所困

    为情所困2017-05-19 10:35:10

    原型继承和构造函数有很多的不同,原型继承即原型链继承。

    原型链并非十分完美, 它包含如下两个问题.

    问题一: 当原型链中包含引用类型值的原型时,该引用类型值会被所有实例共享;

    问题二: 在创建子类型(例如创建Son的实例)时,不能向超类型(例如Father)的构造函数中传递参数.

    有鉴于此, 实践中很少会单独使用原型链.

    为此,下面将有一些尝试以弥补原型链的不足.

    借用构造函数

    为解决原型链中上述两个问题, 我们开始使用一种叫做借用构造函数(constructor stealing)的技术(也叫经典继承).

    基本思想:即在子类型构造函数的内部调用超类型构造函数.

    function Father(){
        this.colors = ["red","blue","green"];
    }
    function Son(){
        Father.call(this);//继承了Father,且向父类型传递参数
    }
    var instance1 = new Son();
    instance1.colors.push("black");
    console.log(instance1.colors);//"red,blue,green,black"
    
    var instance2 = new Son();
    console.log(instance2.colors);//"red,blue,green" 可见引用类型值是独立的

    很明显,借用构造函数一举解决了原型链的两大问题:

    其一, 保证了原型链中引用类型值的独立,不再被所有实例共享;

    其二, 子类型创建时也能够向父类型传递参数.

    随之而来的是, 如果仅仅借用构造函数,那么将无法避免构造函数模式存在的问题--方法都在构造函数中定义, 因此函数复用也就不可用了.而且超类型(如Father)中定义的方法,对子类型而言也是不可见的. 考虑此,借用构造函数的技术也很少单独使用.
    更多请参考 JS原型链与继承别再被问倒了,喜欢就点赞支持一下,谢谢!

    回复
    0
  • 取消回复