一个属性已使用原型更改,但在构造函数中它不是

时间:2014-02-27 07:04:59

标签: javascript prototype

function Ninja(){
  this.swung = true;
}

var ninjaA = new Ninja();
var ninjaB = new Ninja();

Ninja.prototype.swing = function(){
  this.swung = false;
  return this;
};
console.log(ninjaA.swung,"checking Ninja's Swung member")//returning true WHY??
log( ninjaA.swing().swung, "Verify that the swing method exists and returns an instance." ); //false
log( !ninjaB.swing().swung, "and that it works on all Ninja instances." ); //true

如果我操纵原型中的属性,那么我只能通过原型访问它。 正如我正在做的那样ninja.swing()。swung - 使用ninja.prototype.swing改变了poperty值,但为什么我在做ninjaA.swung时会变为真实?

2 个答案:

答案 0 :(得分:0)

不确定您预期会发生什么,但swung是您使用特殊此变量通过共享成员操作的特定于实例的成员。

即使你在Ninja.prototype上定义了swung,默认也是如此,但调用this.swung = false不会改变Ninja.prototype.swung,因为赋值会导致在实例上创建swung而不是查找原型链。

因此,如果您想通过实例更改共享成员并让它反映所有实例中的更改,您必须改变共享成员。你可以这样做:

function Ninja(){
    // you want swung to be shared
    // instance specific members are defined here
//  this.swung = true;
}
Ninja.prototype.shared = {swung:true};
var ninjaA = new Ninja();
var ninjaB = new Ninja();

Ninja.prototype.swing = function(){
  this.shared.swung = false;
  return this;
};
console.log(ninjaB.shared.swung);//=true
ninjaA.swing();
console.log(ninjaB.shared.swung);//=false

以下答案更详细地解释了这一点:https://stackoverflow.com/a/16063711/1641941 该部分解释了从上面的链接原型变异共享成员: 构造函数功能介绍

您可以使用函数作为构造函数来创建对象,如果构造函数名为Person,则使用该构造函数创建的对象是Person的实例。

var Person = function(name){
  this.name = name;
};
Person.prototype.walk=function(){
  this.step().step().step();
};
var bob = new Person("Bob");

Person是构造函数。使用Person创建实例时,必须使用新关键字:

var bob = new Person("Bob");console.log(bob.name);//=Bob
var ben = new Person("Ben");console.log(ben.name);//=Ben

属性/成员name是特定于实例的,对于bob和ben来说它是不同的

成员walk是Person.prototype的一部分,并且对所有实例共享bob和ben是Person的实例,因此它们共享walk成员(bob.walk === ben.walk)。

bob.walk();ben.walk();

因为在bob上找不到walk(),JavaScript会在Person.prototype中查找它,因为这是bob的构造函数。如果在那里找不到它,它将会在Object.prototype上查找。这被称为原型链。继承的原型部分是通过延长这个链来完成的;例如bob => Employee.prototype => Person.prototype => Object.prototype(稍后继承更多关于继承)。

尽管bob,ben和所有其他创建的Person实例共享walk,但每个实例的函数行为都不同,因为在walk函数中它使用thisthis的值将是调用对象;现在让我们说它是bob.walk()"这个"的当前实例。将是鲍勃。 (更多关于"这个"以及之后的调用对象。)

如果本正在等待红灯而且鲍勃处于绿灯状态;然后你会在ben和bob上调用walk(),显然ben和bob会发生不同的事情。

当我们执行类似ben.walk=22之类的操作时会发生隐藏成员,即使bob和ben共享walk 22的赋值到ben.walk也不会影响bob.walk。这是因为该语句将直接在ben上创建一个名为walk的成员,并为其赋值22.将有2个不同的walk成员:ben.walk和Person.prototype.walk。

当要求bob.walk时,您将获得Person.prototype.walk函数,因为在bob上找不到walk。然后要求ben.walk将获得值22,因为成员行走已在ben上创建,因为JavaScript发现在Ben上行走它不会在Person.prototype中查找。

有关原型的更多信息

对象可以通过使用if原型从另一个对象继承。您可以使用Object.create使用任何其他对象设置任何对象的原型。在构造函数的介绍中,我们已经看到,如果在对象上找不到成员,那么JavaScript将在prototpe链中查找它。

在上一部分中,我们已经看到重新分配来自实例原型(ben.walk)的成员将影响该成员(在ben上创建walk而不是更改Person.prototype.walk)。

如果我们不分配但改变会员怎么办?变异是(例如)改变Object的子属性或调用将改变对象值的函数。例如:

var a = [];
a.push(11);
a[1]=22;

以下代码通过变更成员来演示原型成员和实例成员之间的区别。

var person = {
  name:"default",//immutable so can be used as default
  sayName:function(){
    console.log("Hello, I am "+this.name);
  },
  food:[]//not immutable, should be instance specific
         //  not suitable as prototype member
};
var ben = Object.create(person);
ben.name = "Ben";
var bob = Object.create(person);
console.log(bob.name);//=default, setting ben.name shadowed the member
                      //  so bob.name is actually person.name
ben.food.push("Hamburger");
console.log(bob.food);//=["Hamburger"], mutating a shared member on the
// prototype affects all instances as it changes person.food
console.log(person.food);//=["Hamburger"]

上面的代码显示ben和bob分享来自个人的成员。只有一个人,它被设置为bob&和本的原型(人被用作原型链中的第一个对象,用于查找在实例上不存在的请求成员) 。上面代码的问题是bob和ben应该有自己的food成员。这是构造函数的用武之地。它用于创建特定于实例的成员。您还可以向其传递参数以设置这些特定于实例的成员的值。

下一个代码显示了实现构造函数的另一种方法,语法不同但想法是一样的:

  1. 定义一个对象,其成员对于许多实例都是相同的(人是bob和ben的蓝图,可以是jilly,marie,clair ......)
  2. 定义实例特定成员,这些成员对于实例(bob和ben)应该是唯一的。
  3. 在步骤2中创建运行代码的实例。
  4. 使用构造函数,您将在以下代码中的步骤2中设置原型,我们在步骤3中设置原型。

    在这段代码中,我已经从原型和食物中删除了名称,因为无论如何,在创建实例时,您很可能会立即隐藏它。 Name现在是一个特定于实例的成员,在构造函数中设置了默认值。 Becaus食品成员也从原型成员转移到具体成员,在向Ben添加食物时不会影响bob.food。

    var person = {
      sayName:function(){
        console.log("Hello, I am "+this.name);
      },
      //need to run the constructor function when creating
      //  an instance to make sure the instance has
      //  instance specific members
      constructor:function(name){
        this.name = name || "default";
        this.food = [];
        return this;
      }
    };
    var ben = Object.create(person).constructor("Ben");
    var bob = Object.create(person).constructor("Bob");
    console.log(bob.name);//="Bob"
    ben.food.push("Hamburger");
    console.log(bob.food);//=[]
    

    您可能会遇到类似的模式,这些模式更强大,可以帮助创建对象和定义对象。

答案 1 :(得分:0)

function Ninja(){
  this.swung = true;
}
var ninjaA = new Ninja();
…
console.log(ninjaA.swung,"checking Ninja's Swung member")
     

返回true为什么?

因为这是你在构造函数中初始化它的方式。

然后你拨打.swing(),将swung设置为false(有点违反直觉,但没关系)。之后,您按预期记录false

它同样适用于您的ninjaB,但您已将日志记录反转(.swung == false,但您记录! ninjaB.swung)。