Dar*_*ari 9 javascript prototype prototypejs prototypal-inheritance prototype-programming
你知道Javascript是一种基于原型的编程语言.
我已经阅读了一些关于Javascript及其原型继承概念的书籍,但是:
"如果你无法解释给一个六岁,你真的不明白自己了."嗯,我试图解释的JavaScript原型的概念,一个22岁的朋友,完全没有!
您如何向一个对该主题感兴趣的6岁的人解释?
我已经看到Stack Overflow中给出的一些示例,但它没有帮助.
clo*_*eet 13
经典继承是关于扩展事物的类型.说你有课,比如Bike.当你想扩展行为时,你必须设计一种新型的自行车(如MotorBike).
这就像建造一个工厂 - 你制作了大量的蓝图,以及引用这些蓝图的蓝图,但是为了骑一个蓝图,你必须采取蓝图并从中获取一些东西.
基于原型的继承是关于扩展事物本身.假设你有办法制作Bike物品.你把这些中Bike的一个带进你的车库,然后用喷气式发动机装上它.
这不符合蓝图.这是你对这辆特殊自行车所做的事情.但是你的朋友也看到了你的装置,也想要一个.因此,不要为您的新设计制作蓝图,而是竖起一个标语" JetBike工厂"并开始制作更多它们.每当你不记得某些东西是如何组合在一起的,而不是看蓝图,你只需看看原来的自行车.您的原始自行车是原型自行车,所有新自行车都是基于它的.
现在,对于一个真正的6岁孩子来说,这可能就是我要停下来的地方(如果我还没有丢失它们),但实际上基于原型的继承并不只是构建副本,它做的事情甚至更酷 - 它实际上将新JetBike物体链接到车库中的原始原型车.如果您更换原型自行车上的悬架,那么您所有朋友的自行车也会神奇地更换悬架.
让我们看看一些JS-ish伪代码:
function Bike() {
this.wheels = 2;
}
Bike.prototype = {
ride: function() {
// Ride the bike
},
crash: function() {
// Fall off the bike
}
};
function JetBike() {
this.engines = 2;
}
// Start with an ordinary bike
JetBike.prototype = new Bike();
// Modify it
JetBike.prototype.fly = function () {
// Engage thrusters and head for the ramp
};
Run Code Online (Sandbox Code Playgroud)
与大多数其他面向对象的语言不同,JavaScript 实际上没有类的概念。在大多数其他面向对象的语言中,您会实例化特定类的实例,但在 JavaScript 中并非如此。
在 JavaScript 中,对象可以创建新对象,对象可以从其他对象继承。
这整个概念称为原型继承。
但是我们如何制作一个对象呢?
只需使用{}.
var a = {};
a.prop = "myprop";
console.log(a); //Object { prop="myprop" }
Run Code Online (Sandbox Code Playgroud)
你不能创建实例,a因为它不是一个函数。换句话说,它没有特殊的内部方法[[Construct]]。
在 JavaScript 中,任何函数也可以实例化为对象。下面的函数是一个简单的函数,它接受一个名称并将其保存到当前上下文中:
function User( name ) {
this.name = name;
}
Run Code Online (Sandbox Code Playgroud)
我们可以看到这User是 Function 的实例:
alert(User instanceof Function); //true
Run Code Online (Sandbox Code Playgroud)
使用指定的名称创建该函数的新实例:
var me = new User( "My Name" );
Run Code Online (Sandbox Code Playgroud)
我们可以看到它name已经被设置为它自己的一个属性:
alert( me.name == "My Name" ); //true
Run Code Online (Sandbox Code Playgroud)
并且它是User对象的一个实例:
alert( me.constructor == User ); //true
Run Code Online (Sandbox Code Playgroud)
现在,既然User()它只是一个函数,那么当我们这样对待它时会发生什么?
User( "Test" );
Run Code Online (Sandbox Code Playgroud)
由于它的this上下文没有设置,它默认为全局window对象,这意味着它window.name等于name提供的:
alert( window.name == "Test" ); //true
Run Code Online (Sandbox Code Playgroud)
该 constructor 属性存在于每个对象上,并且将始终指向创建它的函数。这样,您应该能够有效地复制对象,创建一个具有相同基类但不具有相同属性的新对象。下面是一个例子:
var you = new me.constructor();
Run Code Online (Sandbox Code Playgroud)
我们可以看到构造函数实际上是一样的:
alert( me.constructor == you.constructor ); //true
Run Code Online (Sandbox Code Playgroud)
Prototype 只包含一个对象,该对象将充当其父对象的所有新副本的基本引用。本质上,原型的任何属性都将在该对象的每个实例上可用。这个创建/引用过程为我们提供了一个廉价的继承版本。
由于对象原型只是一个对象,您可以为它们附加新属性,就像任何其他对象一样。将新属性附加到原型将使它们成为从原始原型实例化的每个对象的一部分,从而有效地公开所有属性。例子:
function User( name, age ){
this.name = name;
this.age = age;
}
Run Code Online (Sandbox Code Playgroud)
将方法和属性添加到构造函数的原型属性是另一种向该构造函数生成的对象添加功能的方法。让我们再添加一个属性CardNo和一个getName()方法:
User.prototype.CardNo='12345';
User.prototype.getName = function(){
return this.name;
};
Run Code Online (Sandbox Code Playgroud)
并向原型添加另一个函数。请注意,上下文将在实例化对象内。
User.prototype.getAge = function(){
return this.age;
};
Run Code Online (Sandbox Code Playgroud)
实例化一个新的 User 对象:
var user = new User( "Bob", 44 );
Run Code Online (Sandbox Code Playgroud)
我们可以看到我们附加的两个方法是与对象一起使用的,具有适当的上下文:
alert( user.getName() == "Bob" ); //true
alert( user.getAge() == 44 ); //true
Run Code Online (Sandbox Code Playgroud)
所以javascript中的每个函数都有一个prototype属性。它的初始值是一个空对象 ({})。请注意,通用对象(不是函数)没有原型属性:
alert( user.prototype ); //undefined (and it is not useful even you define it)
Run Code Online (Sandbox Code Playgroud)
当您尝试访问 的属性时user,假设user.nameJavaScript 引擎将查看对象的所有属性,搜索被调用的对象name,如果找到,则返回其值:
alert( user.name );
Run Code Online (Sandbox Code Playgroud)
如果 javascript 引擎找不到该属性怎么办?它将识别用于创建此对象的构造函数的原型(与您所做的相同user.constructor.prototype)。如果在原型中找到该属性,则使用此属性:
alert(user.CardNo); // "12345"
Run Code Online (Sandbox Code Playgroud)
等等...
如果您想区分对象自己的属性与原型的属性,请使用hasOwnProperty(). 尝试:
alert( user.hasOwnProperty('name') ); //true
alert( user.hasOwnProperty('CardNo') ); //false
Run Code Online (Sandbox Code Playgroud)
当您直接为函数设置属性时,它将是私有的。例子:
function User()
{
var prop="myprop";
function disp(){
alert("this is a private function!");
}
}
var we = new User();
alert(we.prop); //undefined
we.disp(); // Fails, as disp is not a public property of the object
Run Code Online (Sandbox Code Playgroud)
特权方法是Douglas Crockford创造的一个术语,指的是能够查看和操作私有变量(在对象内),同时仍可供用户作为公共方法访问的方法。示例:
创建一个新的 User 对象构造函数:
function User( name, age ) {
//Attempt to figure out the year that the user was born:
var year = (new Date()).getFullYear() – age;
//Create a new Privileged method that has access to the year variable, but is still publically available:
this.getYearBorn = function(){
return year;
};
}
Run Code Online (Sandbox Code Playgroud)
创建用户对象的新实例:
var user = new User( "Bob", 44 );
Run Code Online (Sandbox Code Playgroud)
验证返回的年份是否正确:
alert( user.getYearBorn() == 1962 ); //true
Run Code Online (Sandbox Code Playgroud)
请注意,我们无法访问对象的私有年份属性:
alert( user.year == null ); //true
Run Code Online (Sandbox Code Playgroud)
本质上,特权方法是动态生成的方法,因为它们是在运行时添加到对象中的,而不是在第一次编译代码时。虽然这种技术在计算上比将简单方法绑定到对象原型更昂贵,但它也更强大和灵活。
静态方法背后的前提实际上与任何其他普通函数的前提相同。然而,主要区别在于函数作为对象的静态属性存在。作为属性,它们不能在该对象实例的上下文中访问;它们仅在与主对象本身相同的上下文中可用。对于那些熟悉传统类继承的人来说,这有点像静态类方法。
实际上,以这种方式编写代码的唯一优势是保持对象命名空间干净。
附加到 User 对象的静态方法:
function User(){}
User.cloneUser = function( user ) {
//Create, and return, a new user
return new User( user.getName(), user.getAge() );
};
Run Code Online (Sandbox Code Playgroud)
该cloneUser功能只能通过User以下方式访问:
var me = new User();
me.cloneUser(me); //Uncaught TypeError: Object #<User> has no method 'cloneUser'
Run Code Online (Sandbox Code Playgroud)