Javascript 访问原型属性而不使用;新";实例化函数对象

Javascript 访问原型属性而不使用;新";实例化函数对象,javascript,Javascript,在Crockford的书《好的部分》中,他提到避免使用“新的”,因为这是一种反模式的语言,因为这种语言是原型而不是古典的。然而,根据他的建议,我无法使用prototype属性。我尝试了几种不同的方法: //在下面的示例中,我们无法访问prototype属性,因为当父对象使用可以访问prototype属性的函数对象初始化时,当执行父对象时,我们返回一个对象文本,而该对象文本无权访问该属性。下面的错误是“TypeError:p.info不是函数”。发生这种情况是因为p的info没有定义,因为p是一个

在Crockford的书《好的部分》中,他提到避免使用“新的”,因为这是一种反模式的语言,因为这种语言是原型而不是古典的。然而,根据他的建议,我无法使用prototype属性。我尝试了几种不同的方法:

//在下面的示例中,我们无法访问prototype属性,因为当父对象使用可以访问prototype属性的函数对象初始化时,当执行父对象时,我们返回一个对象文本,而该对象文本无权访问该属性。下面的错误是“TypeError:p.info不是函数”。发生这种情况是因为p的info没有定义,因为p是一个对象文本。父对象具有prototype属性

var parent = function(name, age){
    var name = name || "";
    var age = age || "";
    var that = {};

    that.name = function(){
            return name;
    }

    that.age = function(){
        return age;
    }
    return that;
}

parent.prototype.info = function(){
    return "name: " + this.name() + " age: " + this.age();  
}

var p = parent("John",25);
console.log("name: " + p.name() + " age: " + p.age() + " info: " + p.info());
//在这里,我们遇到了上述同样的问题:

var parent = function(name, age){
    var name = name || "";
    var age = age || "";

    return {
        name: function(){
            return name;
        },
        age: function(){
            return age;
        }
    }
}

parent.prototype.info = function(){
    return "name: " + this.name() + " age: " + this.age();  
}

var p = parent("John",25);
console.log("name: " + p.name() + " age: " + p.age() + " info: " + p.info());
//这也不起作用,因为“p”将是未定义的,因为函数的返回值未定义。此外,“this”指的是全局对象,它是浏览器中的窗口

var parent = function(name, age){
    var name = name || "";
    var age = age || "";

    this.name = function(){
        return name;
    }
    this.age = function(){
        return age;
    }
}

parent.prototype.info = function(){
    return "name: " + this.name() + " age: " + this.age();  
}

var p = parent("John",25);
console.log("name: " + p.name() + " age: " + p.age() + " info: " + p.info());
//但是使用“new”关键字来构造对象允许我们访问原型。这一定意味着父对象的返回值是一个函数,而不是一个常规对象。根据Stoyan Stefanov在《Javascript模式》一书中的说法,当使用新关键字时,会在封面下创建一个空白对象,该对象继承了父级(函数)原型:object.create(Person.prototype)。然后“this”的所有引用都附加到该对象,并返回

var Parent = function(name, age){
    var name = name || "";
    var age = age || "";

    this.name = function(){
        return name;
    }
    this.age = function(){
        return age;
    }
}

Parent.prototype.info = function(){
    return "name: " + this.name() + " age: " + this.age();  
}

var p = new Parent("John",25);
console.log("name: " + p.name() + " age: " + p.age() + " info: " + p.info());
//不幸的是,我无法模拟这种情况。我得到错误:“TypeError:this.prototype不是对象或null”。显然,在使用时,“this”还不是父级

var parent = function(name, age){
    var name = name || "";
    var age = age || "";
    var that = Object.create(this.prototype);

    that.name = function(){
        return name;
    }
    that.age = function(){
        return age;
    }

    return that;
}

parent.prototype.info = function(){
    return "name: " + this.name() + " age: " + this.age();  
}

var p = parent("John",25);
console.log("name: " + p.name() + " age: " + p.age() + " info: " + p.info())

因此,当Crockford说避免使用“new”时,我们应该如何向prototype添加属性?

使用
Object.create()
函数您可以这样做:

// base, its prototype will be used to create a new parent
function Parent(name, age) {
    this.name = name || '';
    this.age = age || '';
};

// factory to create a new parent
function createParent(name, age) {
    var proto = Parent.prototype;
    var properties = {
        name: {writable: true, configurable: true, value: name || ''},
        age: {writable: true, configurable: true, value: age || ''}
    };
    var parent = Object.create(proto, properties);
    return parent;
}

// augment the Parent's prototype
Parent.prototype.info = function(){
    return "name: " + this.name + " age: " + this.age;  
}

// Create a new Parent
var p = createParent("John",25);

// Test
console.log("name: " + p.name + " age: " + p.age + " info: " + p.info());
您可以查看有关如何使用
Object.create()
函数的更多选项/变体

根据需要,您可以使用不同的方法实现类似的功能。一个简单的例子:

var parentBase = {};
var parent = function(name, age){
    var name = name || "";
    var age = age || "";
    var that = parentBase;

    that.name = function(){
            return name;
    }

    that.age = function(){
        return age;
    }
    return that;
}

parentBase.info = function(){
    return "name: " + this.name() + " age: " + this.age();  
}

var p = parent("John",25);
console.log("name: " + p.name() + " age: " + p.age() + " info: " + p.info());
您最好使用
Object.create
,因为它在ECMAS 5中,为您提供了更多的选项、灵活性和功能,您不必自己实现。create实现Douglas Crockford描述的模式。如果平台不支持它,那么您可以按照Crockford的建议进行自定义实现

您可以使用
对象.beget
类似于
对象.create
,如上所述。你可以试试:

if (typeof Object.beget !== 'function') {

     Object.beget = function (o) {

         var F = function () {};

         F.prototype = o;

         return new F();
     };
}

// base, its prototype will be used to create a new parent
function Parent(name, age) {
    this.name = name || '';
    this.age = age || '';
};

// factory to create a new parent
function createParent(name, age) {
    var proto = Parent.prototype;
    var parent = Object.beget(proto);
    parent.name = name || '';
    parent.age = age || '';
    return parent;
}

// augment the Parent's prototype
Parent.prototype.info = function(){
    return "name: " + this.name + " age: " + this.age;  
}

// Create a new Parent
var p = createParent("John",25);

// Test
console.log("name: " + p.name + " age: " + p.age + " info: " + p.info());
试试(这种模式)


JSFIDLE

您可以使用Object.create()或更新的setPrototypeOf(),也可以只使用extend()。您误用了
这个
@dandavis对象。create()是ES5引入的。在此之前,当他鼓励避免“新”的时候,他的书就已经出版了。所以我想要一个不涉及ES5的解决方案,如果这种解决方案存在的话。@JohnMerlino:我想道格在当时称之为begat(),但基本上是一样的。大多数Object.create polyfill看起来像Dougs old Object maker.Pre-ES5,我认为除了使用
\uuu proto\uu
之外,您无法完全避免
新建
Object.create
是一个不错的选择。我尝试了他的Object.beget,但仍然不知道如何将属性附加到原型,即使在使用他的Object.beget时也是如此。这在firefox中返回了一个错误:我编辑了我的答案以包含Object.beget()的工作示例。@JohnMerlino
Object.beget
接受一个原型对象并返回一个新创建的对象,其原型链以传入的对象开始(即
Object.beget(p)
返回一个对象
obj
,该对象的
obj.\uuu proto\uuu==p
)。在小提琴中,您传递的是
parent
的返回值,但从
parent(“John”,25)
返回的结果只是一个普通对象,与存储在
parent.prototype
中的原型无关。您需要执行
Object.beget(person.prototype)
并设置从
Object.beget
返回的对象的实例属性。
var parent = function(name, age){
    var name = name || "";
    var age = age || "";
    // add `info` as `var`
    var that = {}, info;

    // change method to function expression
    that.name = (function(){
            return name;
    }())
    // change method to function expression
    that.age = (function(){
        return age;
    }())
    return that;
// add `|| {}` , for accessing `parent.info`
} || {};

// add param `that`  to `parent.info` function
parent.info = function(that){
    return "name: " + that.name + " age: " + that.age;  
};

var p = parent("John",25); 
console.log("name: " + p.name + " age: " + p.age + " info: " + parent.info(p));

// var results = document.createElement("div");

// results.innerHTML = parent.info(p);

// document.body.appendChild(results);