原型继承 - 编写 [英] Prototypical inheritance - writing up

查看:31
本文介绍了原型继承 - 编写的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我有这两个例子,来自 javascript.info:

So I have these 2 examples, from javascript.info:

示例 1:

var animal = {
  eat: function() {
    alert( "I'm full" )
    this.full = true
  }
}

var rabbit = {
  jump: function() { /* something */ }
}

rabbit.__proto__ = animal 

rabbit.eat() 

示例 2:

function Hamster() {  }
Hamster.prototype = {
  food: [],
  found: function(something) {
    this.food.push(something)
  }
}

// Create two speedy and lazy hamsters, then feed the first one
speedy = new Hamster()
lazy = new Hamster()

speedy.found("apple")
speedy.found("orange")

alert(speedy.food.length) // 2
alert(lazy.food.length) // 2 (!??)

从例2开始:当代码到达speedy.found时,在speedy中没有找到found属性,所以爬上去到原型并在那里更改它.这就是为什么两只仓鼠的 food.length 相等,换句话说,它们的胃是一样的.

由此我了解到,当编写并添加一个不存在的新属性时,解释器将沿着原型链向上移动,直到找到该属性,然后对其进行更改.

Start from Example 2: when the code reaches speedy.found, it finds no found property in speedy, and so it climbs up to the prototype and changes it there. That's why food.length is equal for both hamsters, in other words they have the same stomach.

From this I understand, that when writing up and adding a new property which doesn't exist, the interpreter will go up the prototype chain until it finds the property, and THEN change it.

但是在示例 1 中发生了其他事情:
我们运行rabbit.eat,它改变了rabbit.full.full 属性无处可寻,所以它应该沿着原型链向上(到对象??),好吧,我不确定这里会发生什么.在这个例子中,rabbit 的属性 full 被创建和改变,而在第一个例子中,它上升到原型链,因为它找不到属性.

BUT in Example 1 something else happens:
we run rabbit.eat, which changes rabbit.full. full property is nowhere to be found, so it should go up the prototype chain to (to object??), and well, I'm not sure what happens here. In this example the property full of rabbit is created and changed, while in the first example it goes up the prototype chain because it cannot find the property.

我很困惑,不明白为什么会这样.

I'm confused and cannot see why this happens.

推荐答案

构造函数介绍

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

You can use a function as a constructor to create objects, if the constructor function is named Person then the object(s) created with that constructor are instances of Person.

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

Person 是构造函数.当您使用 Person 创建实例时,您必须使用 new 关键字:

Person is the constructor function. When you create an instance using Person you have to use the new keyword:

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

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

The property/member name is instance specific, it's different for bob and ben

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

The member walk is part of Person.prototype and is shared for all instances bob and ben are instances of Person so they share the walk member (bob.walk===ben.walk).

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

因为在 bob 上无法直接找到 walk(),JavaScript 会在 Person.prototype 中寻找它,因为这是 bob 的构造函数.如果在那里找不到它,它将在 Object.prototype 上查找.这称为原型链.继承的原型部分就是通过加长这条链来完成的;例如 bob => Employee.prototype => Person.prototype => Object.prototype(稍后会详细介绍继承).

Because walk() could not be found on bob directly JavaScript will look for it in the Person.prototype as this is the constructor of bob. If it can't be found there it'll look on Object.prototype. This is called the prototype chain. The prototype part of inheritance is done by lengthening this chain; for example bob => Employee.prototype => Person.prototype => Object.prototype (more on inheritance later).

即使 bob、ben 和所有其他创建的 Person 实例共享 walk 函数,每个实例的行为也会有所不同,因为在 walk 函数中它使用 this.this 的值将是调用对象;现在假设它是当前实例,所以对于 bob.walk() 来说,this"将是 bob.(稍后将详细介绍this"和调用对象).

Even though bob, ben and all other created Person instances share walk the function will behave differently per instance because in the walk function it uses this. The value of this will be the invoking object; for now let's say it's the current instance so for bob.walk() "this" will be bob. (more on "this" and the invoking object later).

如果本在等红灯而鲍勃在等绿灯;然后您将对 ben 和 bob 调用 walk() 显然 ben 和 bob 会发生一些不同的事情.

If ben was waiting for a red light and and bob was at a green light; then you'll invoke walk() on both ben and bob obviously something different would happen to ben and bob.

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

Shadowing members happens when we do something like ben.walk=22, even though bob and ben share walk the assignment of 22 to ben.walk will not affect bob.walk. This is because that statement will create a member called walk on ben directly and assign it a value of 22. There will be 2 different walk members: ben.walk and Person.prototype.walk.

当请求 bob.walk 时,你会得到 Person.prototype.walk 函数,因为在 bob 上找不到 walk.然而,请求 ben.walk 会得到值 22,因为成员 walk 是在 ben 上创建的,而且由于 JavaScript 在 ben 上找到了 walk,它不会在 Person.prototype 中查找.

When asking for bob.walk you'll get the Person.prototype.walk function because walk could not be found on bob. Asking for ben.walk however will get you the value 22 because the member walk has been created on ben and since JavaScript found walk on ben it will not look in the Person.prototype.

当使用带有 2 个参数的 Object.create 时,Object.defineProperty 或 Object.defineProperties 阴影的工作方式略有不同.更多信息 此处.

When using Object.create with 2 arguments, Object.defineProperty or Object.defineProperties shadowing works a bit different. More info on that here.

更多关于原型

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

An object can inherit from another object through the use of prototype. You can set the prototype of any object with any other object using Object.create. In the constructor function introduction we have seen that if a member can't be found on the object then JavaScript will look in the prototpe chain for it.

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

In previous part we have seen that re assignment of members that come from an instance's prototype (ben.walk) will shadow that member (create walk on ben rather than changing Person.prototype.walk).

如果我们不重新分配而是改变成员怎么办?变异是(例如)更改对象的子属性或调用将更改对象值的函数.例如:

What if we don't re assign but mutate the member? Mutating is (for example) changing sub properties of an Object or invoking functions that will change the object's value. For example:

var o = [];
var a = o;
a.push(11);//mutate a, this will change o
a[1]=22;//mutate a, this will change o

以下代码通过修改成员来演示原型成员和实例成员之间的区别.

The following code demonstrates the difference between prototype members and instance members by mutating members.

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 从person 共享成员.只有一个人,它被设置为 bob 和 ben 的原型(person 用作原型链中的第一个对象,用于查找实例上不存在的请求成员).上面代码的问题是 bob 和 ben 应该有自己的 food 成员.这就是构造函数的用武之地.它用于创建特定于实例的成员.您还可以将参数传递给它以设置这些特定于实例的成员的值.

The code above shows that ben and bob share members from person. There is only one person, it is set as bob's and ben's prototype (person is used as the first object in the prototype chain to look up requested members that don't exist on the instance). The problem with the above code is that bob and ben should have their own food member. This is where the constructor function comes in. It is used to create instance specific members. You could also pass arguments to it to set values of these instance specific members.

接下来的代码展示了另一种实现构造函数的方式,语法不同但思路是一样的:

The next code shows another way to implement the constructor function, syntax is different but the idea is the same:

  1. 定义一个对象,它的成员在很多情况下都是相同的(person 是 bob 和 ben 的蓝图,可以是 jilly、marie、clair ...)
  2. 定义实例特定成员,这些成员对于实例(bob 和 ben)应该是唯一的.
  3. 创建一个运行第 2 步中的代码的实例.

使用构造函数,您将在以下代码的第 2 步中设置原型,我们在第 3 步中设置原型.

With constructor functions you'll set the prototype in step 2 in the following code we set the prototype in step 3.

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

In this code I have removed name from prototype as well as food because you are most likely going to shadow this almost immediately when creating an instance anyway. Name is now an instance specific member with a default value set in the constructor function. Becaus the food member is also moved from prototype to instance specific member it will not affect bob.food when adding food to ben.

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);//=[]

您可能会遇到类似的模式,它们在帮助对象创建和对象定义方面更加强大.

You may come across similar patterns that are more robust to help with object creation and object definition.

继承

下面的代码展示了如何继承.任务基本和之前的代码一样,只是多了一点

The following code shows how to inherit. The tasks are basically the same as in code before with a little extra

  1. 定义对象的实例特定成员(函数 Hamster 和 RussionMini).
  2. 设置继承的原型部分(RussionMini.prototype = Object.create(Hamster.prototype))
  3. 定义可以在实例之间共享的成员.(Hamster.prototype 和 RussionMini.prototype)
  4. 创建一个实例运行第 1 步中的代码,并为继承的对象运行父代码(Hamster.apply(this,arguments);)

使用一些人会称之为经典继承"的模式.如果您对语法感到困惑,我很乐意解释更多或提供不同的模式.

Using a pattern some would call "classical inheritance". If you are confused by the syntax I'll be happy to explain more or provide different patterns.

function Hamster(){
 this.food=[];
}
function RussionMini(){
  //Hamster.apply(this,arguments) executes every line of code
  //in the Hamster body where the value of "this" is
  //the to be created RussionMini (once for mini and once for betty)
  Hamster.apply(this,arguments);
}
//setting RussionMini's prototype
RussionMini.prototype=Object.create(Hamster.prototype);
//setting the built in member called constructor to point
// to the right function (previous line has it point to Hamster)
RussionMini.prototype.constructor=RussionMini;
mini=new RussionMini();
//this.food (instance specic to mini)
//  comes from running the Hamster code
//  with Hamster.apply(this,arguments);
mini.food.push("mini's food");
//adding behavior specific to Hamster that will still be
//  inherited by RussionMini because RussionMini.prototype's prototype
//  is Hamster.prototype
Hamster.prototype.runWheel=function(){console.log("I'm running")};
mini.runWheel();//=I'm running

Object.create 设置继承的原型部分

这里是关于 Object.create 的文档,它基本上返回第二个参数(polyfil 不支持),第一个参数作为返回对象的原型.

Here is the documentation about Object.create, it basically returns the second argument (not supported in the polyfil) with the first argument as the returned object's prototype.

如果没有给出第二个参数,它将返回一个空对象,第一个参数用作返回对象的原型(在返回对象的原型链中使用的第一个对象).

If no second argument was given it'll return an empty object with first argument to be used as the returned object's prototype (the first object to be used in the returned object's prototype chain).

有些人会将 RussionMini 的原型设置为 Hamster 的一个实例(RussionMini.prototype = new Hamster()).这是不可取的,因为即使它实现了相同的功能(RussionMini.prototype 的原型是 Hamster.prototype),它也会将 Hamster 实例成员设置为 RussionMini.prototype 的成员.所以 RussionMini.prototype.food 将存在,但它是一个共享成员(还​​记得更多关于原型"中的 bob 和 ben 吗?).在创建 RussionMini 时,food 成员将被遮蔽,因为 Hamster 代码使用 Hamster.apply(this,arguments); 运行,然后运行 ​​this.food = [] 但是任何仓鼠成员仍将是 RussionMini.prototype 的成员.

Some would set the prototype of RussionMini to an instance of Hamster (RussionMini.prototype = new Hamster()). This is not desirable because even though it accomplishes the same (RussionMini.prototype's prototype is Hamster.prototype) it also sets Hamster instance members as members of RussionMini.prototype. So RussionMini.prototype.food will exist but is a shared member (remember bob and ben in "More about prototype"?). The food member will be shadowed when creating a RussionMini because Hamster code is run with Hamster.apply(this,arguments); that in turn runs this.food = [] but any Hamster members will still be members of RussionMini.prototype.

另一个原因可能是要创建一个仓鼠,需要对传递的参数进行大量复杂的计算,这些参数可能尚不可用,同样,您可以传递虚拟参数,但这可能会不必要地使您的代码复杂化.

Another reason could be that to create a Hamster a lot of complicated calculations need be done on passed arguments that may be not available yet, again you could pass in dummy arguments but it could unnecessarily complicate your code.

扩展和覆盖父函数

有时children 需要扩展parent 函数.

Sometimes children need to extend parent functions.

您希望孩子"(=RussionMini)做一些额外的事情.当 RussionMini 可以调用 Hamster 代码做某事然后做一些额外的事情时,您不需要将 Hamster 代码复制并粘贴到 RussionMini.

You want the 'child' (=RussionMini) to do something extra. When RussionMini can call the Hamster code to do something and then do something extra you don't need to copy and paste Hamster code to RussionMini.

在下面的示例中,我们假设仓鼠每小时可以跑 3 公里,而 Russion mini 只能跑一半.我们可以在 RussionMini 中对 3/2 进行硬编码,但是如果要更改此值,我们在代码中有多个需要更改的地方.下面是我们如何使用 Hamster.prototype 来获取父(Hamster)速度.

In the following example we assume that a Hamster can run 3km an hour but a Russion mini can only run half as fast. We can hard code 3/2 in RussionMini but if this value were to change we have multiple places in code where it needs changing. Here is how we use Hamster.prototype to get the parent (Hamster) speed.

var Hamster = function(name){
 if(name===undefined){
   throw new Error("Name cannot be undefined");
 }
 this.name=name;
}
Hamster.prototype.getSpeed=function(){
  return 3;
}
Hamster.prototype.run=function(){
  //Russionmini does not need to implement this function as
  //it will do exactly the same as it does for Hamster
  //But Russionmini does need to implement getSpeed as it
  //won't return the same as Hamster (see later in the code) 
  return "I am running at " + 
    this.getSpeed() + "km an hour.";
}

var RussionMini=function(name){
  Hamster.apply(this,arguments);
}
//call this before setting RussionMini prototypes
RussionMini.prototype = Object.create(Hamster.prototype);
RussionMini.prototype.constructor=RussionMini;

RussionMini.prototype.getSpeed=function(){
  return Hamster.prototype
    .getSpeed.call(this)/2;
}    

var betty=new RussionMini("Betty");
console.log(betty.run());//=I am running at 1.5km an hour.

缺点是您对 Hamster.prototype 进行了硬编码.可能有一些模式可以像在 Java 中一样为您提供 super 的优势.

The disadvantage is that you hard code Hamster.prototype. There may be patterns that will give you the advantage of super as in Java.

我见过的大多数模式要么在继承级别超过 2 级(Child => Parent => GrandParent)时破坏,要么通过 闭包.

Most of the patterns I've seen will either break when inheritance level is more than 2 levels (Child => Parent => GrandParent) or use more resources by implementing super through closures.

要覆盖父 (=Hamster) 方法,您可以执行相同的操作,但不要执行 Hamster.prototype.parentMethod.call(this,....

To override a Parent (=Hamster) method you do the same but don't do Hamster.prototype.parentMethod.call(this,....

this.constructor

constructor 属性被 JavaScript 包含在原型中,您可以更改它,但它应该指向构造函数.所以Hamster.prototype.constructor 应该指向Hamster.

The constructor property is included in the prototype by JavaScript, you can change it but it should point to the constructor function. So Hamster.prototype.constructor should point to Hamster.

如果设置继承的原型部分后,你应该让它再次指向正确的函数.

If after setting prototype part of inheritance you should have it point to the right function again.

var Hamster = function(){};
var RussionMinni=function(){
   // re use Parent constructor (I know there is none there)
   Hamster.apply(this,arguments);
};
RussionMinni.prototype=Object.create(Hamster.prototype);
console.log(RussionMinni.prototype.constructor===Hamster);//=true
RussionMinni.prototype.haveBaby=function(){
  return new this.constructor();
};
var betty=new RussionMinni();
var littleBetty=betty.haveBaby();
console.log(littleBetty instanceof RussionMinni);//false
console.log(littleBetty instanceof Hamster);//true
//fix the constructor
RussionMinni.prototype.constructor=RussionMinni;
//now make a baby again
var littleBetty=betty.haveBaby();
console.log(littleBetty instanceof RussionMinni);//true
console.log(littleBetty instanceof Hamster);//true

混入的多重继承"

有些东西最好不要继承,如果Cat可以移动,那么Cat不应该继承Movable.猫不是可移动的,而是猫可以移动.在基于类的语言中,Cat 必须实现 Movable.在 JavaScript 中,我们可以定义 Movable 并在此处定义实现,Cat 可以覆盖、扩展它,也可以将其作为默认实现.

Some things are better not to be inherited, if a Cat can move and then a Cat should not inherit from Movable. A Cat is not a Movable but rather a Cat can move. In a class based language Cat would have to implement Movable. In JavaScript we can define Movable and define implementation here, Cat can either override, extend it or us it's default implementation.

对于 Movable,我们有特定于实例的成员(例如 location).而且我们有不特定于实例的成员(如函数 move()).创建实例时,将通过调用 mxIns(由 mixin 辅助函数添加)来设置实例特定成员.Prototype 成员将使用 mixin 辅助函数从 Movable.prototype 中一一复制到 Cat.prototype 上.

For Movable we have instance specific members (like location). And we have members that are not instance specific (like the function move()). Instance specific members will be set by calling mxIns (added by mixin helper function) when creating an instance. Prototype members will be copied one by one on Cat.prototype from Movable.prototype using the mixin helper function.

var Mixin = function Mixin(args){
  if(this.mixIns){
    i=-1;len=this.mixIns.length;
    while(++i<len){
        this.mixIns[i].call(this,args);
      }
  }  
};
Mixin.mix = function(constructor, mix){
  var thing
  ,cProto=constructor.prototype
  ,mProto=mix.prototype;
  //no extending, if multiple prototypes
  // have members with the same name then use
  // the last
  for(thing in mProto){
    if(Object.hasOwnProperty.call(mProto, thing)){
      cProto[thing]=mProto[thing];
    }
  }
  //instance intialisers
  cProto.mixIns = cProto.mixIns || [];
  cProto.mixIns.push(mix);
};
var Movable = function(args){
  args=args || {};
  //demo how to set defaults with truthy
  // not checking validaty
  this.location=args.location;
  this.isStuck = (args.isStuck===true);//defaults to false
  this.canMove = (args.canMove!==false);//defaults to true
  //speed defaults to 4
  this.speed = (args.speed===0)?0:(args.speed || 4);
};
Movable.prototype.move=function(){
  console.log('I am moving, default implementation.');
};
var Animal = function(args){
  args = args || {};
  this.name = args.name || "thing";
};
var Cat = function(args){
  var i,len;
  Animal.call(args);
  //if an object can have others mixed in
  //  then this is needed to initialise 
  //  instance members
  Mixin.call(this,args);
};
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
Mixin.mix(Cat,Movable);
var poochie = new Cat({
  name:"poochie",
  location: {x:0,y:22}
});
poochie.move();

上面是一个简单的实现,用最后混合的任何混合替换同名函数.

The above is a simple implementation that replaces same named functions with whatever mix in is mixed in last.

这个变量

在所有示例代码中,您将看到 this 指的是当前实例.

In all the example code you'll see this referring to the current instance.

this 变量实际上是指调用对象,它指的是在函数之前出现的对象.

The this variable actually refers to the invoking object, it refers to the object that came before the function.

为了澄清,请参阅以下代码:

To clarify see the following code:

theInvokingObject.thefunction();

this 引用错误对象的实例通常是在附加事件侦听器、回调或超时和间隔时.在接下来的两行代码中,我们pass 函数,我们不调用它.传递函数是:someObject.aFunction,调用它的是:someObject.aFunction().this 值不是引用函数声明的对象,而是引用调用它的对象.

The instances where this would refer to the wrong object are usually when attaching event listeners, callbacks or timeouts and intervals. In the next 2 lines of code we pass the function, we don't invoke it. Passing the function is: someObject.aFunction and invoking it is: someObject.aFunction(). The this value does not refer to the object the function was declared on but on the object that invokes it.

setTimeout(someObject.aFuncton,100);//this in aFunction is window
somebutton.onclick = someObject.aFunction;//this in aFunction is somebutton

要使 this 在上述情况下引用 someObject 你可以传递一个 closure 而不是直接使用函数:

To make this in the above cases refer to someObject you can pass a closure instead of the function directly:

setTimeout(function(){someObject.aFuncton();},100);
somebutton.onclick = function(){someObject.aFunction();};

我喜欢定义为闭包 对原型中包含的变量进行精细控制 关闭 作用域.

I like to define functions that return a function for closures on the prototype to have fine control over the variables that are included in the closure scope.

var Hamster = function(name){
  var largeVariable = new Array(100000).join("Hello World");
  // if I do 
  // setInterval(function(){this.checkSleep();},100);
  // then largeVariable will be in the closure scope as well
  this.name=name
  setInterval(this.closures.checkSleep(this),1000);
};
Hamster.prototype.closures={
  checkSleep:function(hamsterInstance){
    return function(){
      console.log(typeof largeVariable);//undefined
      console.log(hamsterInstance);//instance of Hamster named Betty
      hamsterInstance.checkSleep();
    };
  }
};
Hamster.prototype.checkSleep=function(){
  //do stuff assuming this is the Hamster instance
};

var betty = new Hamster("Betty");

传递(构造函数)参数

当 Child 调用 Parent (Hamster.apply(this,arguments);) 时,我们假设 Hamster 以相同的顺序使用与 RussionMini 相同的参数.对于调用其他函数的函数,我通常使用另一种方式传递参数.

When Child calls a Parent (Hamster.apply(this,arguments);) we assume that Hamster uses the same arguments as RussionMini in the same order. For functions that call other functions I usually use another way to pass arguments.

我通常将一个对象传递给一个函数,并让该函数改变它需要的任何内容(设置默认值),然后该函数会将其传递给另一个执行相同操作的函数,依此类推.下面是一个例子:

I usually pass one object to a function and have that function mutate whatever it needs (set defaults), then that function will pass it to another function that will do the same and so on and so on. Here is an example:

//helper funciton to throw error
function thowError(message){
  throw new Error(message)
};
var Hamster = function(args){
  //make sure args is something so you get the errors
  //  that make sense to you instead of "args is undefined"
  args = args || {};
  //default value for type:
  this.type = args.type || "default type";
  //name is not optional, very simple truthy check f
  this.name = args.name || thowError("args.name is not optional");
};
var RussionMini = function(args){
  //make sure args is something so you get the errors
  //  that make sense to you instead of "args is undefined"
  args = args || {};
  args.type = "Russion Mini";
  Hamster.call(this,args);
};
var ben = new RussionMini({name:"Ben"});
console.log(ben);// Object { type="Russion Mini", name="Ben"}
var betty = new RussionMini();//Error: args.name is not optional

这种在函数链中传递参数的方式在很多情况下都很有用.当您编写代码来计算某物的总和,然后您想将某物的总和重新分解为某种货币时,您可能需要更改许多函数以传递货币值.您可以扩大货币价值的范围(甚至像 window.currency='USD' 这样的全球范围),但这是解决问题的糟糕方法.

This way of passing arguments in a function chain is useful in many cases. When you're working on code that would calculate a total of something and later you'd like to re factor the total of that something to be in a certain currency you may have to change a lot of functions to pass the value for currency. You could up scope a currency value (even to global like window.currency='USD') but that's a bad way to solve it.

通过传递一个对象,您可以在函数链中可用时向 args 添加货币,并在需要时改变/使用它而无需更改其他函数(明确必须在函数中传递它电话).

With passing an object you could add currency to args whenever it's available in the function chain and mutate/use it whenever you need it without changing the other functions (explicitly have to pass it in the function calls).

私有变量

JavaScript 没有私有修饰符.

JavaScript doesn't have a private modifier.

我同意以下内容:http://blog.millermedeiros.com/a-case-against-private-variables-and-functions-in-javascript/ 并且个人没有使用过它们.

I agree with the following: http://blog.millermedeiros.com/a-case-against-private-variables-and-functions-in-javascript/ and personally have not used them.

您可以通过将成员命名为 _aPrivate 或将所有私有变量放在名为 _ 的对象变量中来向其他程序员表明该成员是私有的.

You can indicate to other programmers a member is meant to be private by naming it _aPrivate or putting all the private variables in an object variable called _.

您可以通过闭包实现私有成员,但是特定于实例的私有成员只能由不在原型上的函数访问.

You can implement private members through closures but instance specific private members can only be accessed by functions that are not on the prototype.

不实现私有作为闭包会泄漏实现并使您或用户扩展您的代码以使用不属于您的公共 API 的成员.这可能是好的也可能是坏的.

Not implementing privates as closures would leak implementation and enable you or users extending your code to use members that are not part of your public API. This can be both good and bad.

这很好,因为它使您和其他人能够轻松地模拟某些成员以进行测试.它让其他人有机会轻松改进(修补)您的代码,但这也很糟糕,因为无法保证您的代码的下一个版本具有相同的实现和/或私有成员.

It's good because it enables you and others to mock certain members for testing easily. It gives others a chance to easily improve (patch) your code but this is also bad because there is no guarantee that a next version of your code has the same implementation and or private members.

通过使用闭包,您不会给其他人选择,而通过使用文档的命名约定,您可以这样做.这不是 JavaScript 特有的,在其他语言中,您可以决定不使用私有成员,因为您相信其他人知道他们在做什么,并让他们选择做他们想做的事(涉及风险).

By using closures you do not give others a choice and by using the naming convention with documentation you do. This is not specific to JavaScript, in other languages you can decide not to use private members as you trust others to know what they are doing and give them the choice to do as they want (with risks involved).

如果您仍然坚持私有,那么以下模式可能会有所帮助.虽然它没有实现私有,但实现了受保护.

If you still insist on privates then the following pattern may help. It doesn't implement private though but implements protected.

这篇关于原型继承 - 编写的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆