您知道Javascript是prototype-based programming language 我已经阅读了一些关于Javascript及其原型继承概念的书籍,但是:
“如果你不能解释它给一个六岁的孩子,你自己真的不明白。”嗯,我试图向一个22岁的朋友解释JavaScript原型概念并完全失败! / p>
你如何向一个对这个问题感兴趣的6岁的人解释呢? 我已经看到了Stack Overflow中给出的一些例子,它没有帮助。
答案 0 :(得分:10)
经典继承是关于扩展事物的类型。假设你有一个班级,比如Bike
。如果要扩展行为,则必须设计一种新型自行车(如MotorBike
)。
这就像建造一个工厂 - 你制作了大量的蓝图,以及引用这些蓝图的蓝图,但是为了骑一个蓝图,你必须采取蓝图并从中制作一些东西。
基于原型的继承是关于扩展事物本身。假设你有办法制作Bike
个对象。你将这些Bike
中的一个带入你的车库,并将喷气式发动机装上它。
这不符合蓝图。这是你对这辆特殊自行车所做的事情。但是你的朋友也看到了你的装置,也想要一个。因此,不要为新设计制作蓝图,而是竖起一个标语“JetBike
factory”并开始制作更多。每当你不记得某些东西是如何组合在一起的,而不是看蓝图,你只需看看原来的自行车。您的原始自行车是原型自行车,所有新自行车都是基于它。
现在,对于一个真正的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
};
答案 1 :(得分:2)
Javascript是一种面向对象的语言,它的独特之处在于它没有类。相反,我们使用函数来创建对象。
所有函数都有一个原型,您使用该函数创建的所有对象都将继承所有属性和方法。由于Javscript没有类,因此使用实际对象继承(而不是类)来执行继承。您可以将函数的原型设置为对象,允许您使用该函数创建的所有对象都继承函数原型对象的所有方法和属性。
所以,如果我有一个创建对象的函数:
function Foo() {
}
Foo.prototype.someProperty = 'blahblahblah';
您可以创建另一个创建对象的函数,并允许它通过将函数原型设置为该对象来继承对象属性和方法。
function Bar() {
}
Bar.prototype = new Foo();
然后你可以访问所有继承的东西。
var bar = new Bar();
alert( bar.someProperty ); // blahblahblah
答案 2 :(得分:2)
与大多数其他面向对象语言不同,JavaScript实际上没有概念
课程。在大多数其他面向对象的语言中,您将实例化特定类的实例,但在JavaScript中则不是这样
在JavaScript中,对象可以创建新对象,对象可以从其他对象继承。
这整个概念称为原型继承。
但我们如何制作一个物品呢?
您只需使用{}
创建通用对象。
var a = {};
a.prop = "myprop";
console.log(a); //Object { prop="myprop" }
您无法创建a
的实例,因为它不是函数。换句话说,它没有特殊的内部方法[[Construct]]
。
在JavaScript中,任何函数也可以实例化为对象。下面的函数是一个简单的函数,它接受一个名称并将其保存到当前上下文中:
function User( name ) {
this.name = name;
}
我们可以看到User
是Function的实例:
alert(User instanceof Function); //true
使用指定的名称:
创建该函数的新实例var me = new User( "My Name" );
我们可以看到它的name
已经被设置为自己的属性:
alert( me.name == "My Name" ); //true
它是User
对象的一个实例:
alert( me.constructor == User ); //true
现在,由于User()
只是一个函数,当我们这样处理时会发生什么?
User( "Test" );
由于未设置this
上下文,因此默认为全局window
对象,这意味着window.name
等于提供的name
:
alert( window.name == "Test" ); //true
constructor
属性存在于每个对象上,并始终指向创建它的函数。这样,您应该能够有效地复制对象,创建一个相同基类但不具有相同属性的新对象。下面是一个例子:
var you = new me.constructor();
我们可以看到构造函数实际上是相同的:
alert( me.constructor == you.constructor ); //true
Prototype只包含一个对象,该对象将作为其父对象的所有新副本的基本引用。从本质上讲,原型的任何属性都可以在该对象的每个实例上使用。这个创建/引用过程为我们提供了一个廉价的继承版本 由于对象原型只是一个对象,因此您可以将新属性附加到它们,就像任何其他对象一样。将新属性附加到原型将使它们成为从原始原型实例化的每个对象的一部分,从而有效地使所有属性公开。例如:
function User( name, age ){
this.name = name;
this.age = age;
}
将方法和属性添加到构造函数的prototype属性是向构造函数生成的对象添加功能的另一种方法。让我们再添加一个属性CardNo
和getName()
方法:
User.prototype.CardNo='12345';
User.prototype.getName = function(){
return this.name;
};
并为原型添加另一个功能。请注意,上下文将位于实例化对象中。
User.prototype.getAge = function(){
return this.age;
};
实例化新的用户对象:
var user = new User( "Bob", 44 );
我们可以看到我们附加的两个方法是对象,具有适当的上下文:
alert( user.getName() == "Bob" ); //true
alert( user.getAge() == 44 ); //true
因此javascript中的每个函数都有一个prototype属性。它的初始值是一个空对象({})。请注意,通用对象(不是函数)没有原型属性:
alert( user.prototype ); //undefined (and it is not useful even you define it)
当您尝试访问user
的属性时,请说user.name
JavaScript引擎将查看对象的所有属性,搜索名为name
的对象,如果找到它会返回它的值:
alert( user.name );
如果javascript引擎无法找到属性怎么办?它将识别用于创建此对象的构造函数原型(就像执行user.constructor.prototype
一样)。如果在原型中找到该属性,则使用此属性:
alert(user.CardNo); // "12345"
等......
如果要区分对象自己的属性与原型属性,请使用hasOwnProperty()
。尝试:
alert( user.hasOwnProperty('name') ); //true
alert( user.hasOwnProperty('CardNo') ); //false
直接为函数设置属性时,它将是私有的。例如:
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
特权方法是由 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;
};
}
创建用户对象的新实例:
var user = new User( "Bob", 44 );
验证返回的年份是否正确:
alert( user.getYearBorn() == 1962 ); //true
请注意,我们无法访问该对象的私有年度属性:
alert( user.year == null ); //true
本质上,特权方法是动态生成的方法,因为它们是被添加的 在运行时,而不是在第一次编译代码时。虽然这种技术在计算上比将简单方法绑定到对象原型更昂贵,但它也更加强大和灵活。
静态方法背后的前提几乎与任何其他正常功能相同。然而,主要区别在于函数作为对象的静态属性存在。作为属性,它们不能在该对象的实例的上下文中访问;它们仅在与主对象本身相同的上下文中可用。对于熟悉传统类继承的人来说,这有点像静态类方法 实际上,以这种方式编写代码的唯一好处是保持对象命名空间的清晰 附加到User对象的静态方法:
function User(){}
User.cloneUser = function( user ) {
//Create, and return, a new user
return new User( user.getName(), user.getAge() );
};
cloneUser
功能只能由User
访问:
var me = new User();
me.cloneUser(me); //Uncaught TypeError: Object #<User> has no method 'cloneUser'
答案 3 :(得分:1)
有趣的挑战: - )
首先,我永远不会试图通过单词来向任何人解释这一点,但我会试一试: - )
“原型继承就像一个可以从其他小精灵那里窃取权力的宠物小精灵。”
认为你可以创造自己的宠物小精灵。你可以决定它有多大,它是什么颜色等(构造函数)。然后你可以给那个口袋妖怪的力量(原型)。您可以根据需要生成尽可能多的这些小宠物。原型继承给你的是让一个,几个或所有这些小精灵从其他小精灵那里窃取权力的可能性。你甚至可以从已经偷走了其他口袋妖怪的权力的小宠物中窃取权力。这创造了一系列超级强大的小宠物。
也许有点傻,但它反映了原型继承是多么强大......在口袋妖怪的意义上: - )
答案 4 :(得分:1)
原型继承就像一个儿子,他的父亲背着他的父亲。如果有人问儿子,“你的鞋子是什么颜色的?”除非他是赤脚,否则他会以鞋子的颜色作出回应,然后他会回答他父亲的鞋子颜色。
答案 5 :(得分:0)
/* Here is simple way how to inherit objects properties and methods from others object by using prototyping inheritance in plain java script.*/
(function() {
// get dom elements for display output`enter code here
var engTeacherPara = document.getElementById("engTeacher");
var chemTeacherPara = document.getElementById("chemTeacher");
// base class
var SchoolStaff = function(name, id) {
this.name = name;
this.id = id;
}
// method on the SchoolStaff object
SchoolStaff.prototype.print = function() {
return "Name : " + this.name + " Employee id: " + this.id;
}
SchoolStaff.prototype.sayHello = function() {
return "Hello Mr : " + this.name;
}
// sub class engTeacher
var EngTeacher = function(name, id, salary) {
SchoolStaff.call(this, name, id);
this.salary = salary;
}
// Inherit the SchoolStaff prototype
EngTeacher.prototype = Object.create(SchoolStaff.prototype);
// Set the engTeacher constructor to engTeacher object
EngTeacher.prototype.constructor = EngTeacher;
// method on engTeacher object
EngTeacher.prototype.print = function() {
return "Name : " + this.name + " Salary : " + this.salary + " Employee id: " + this.id;
}
// sub class chemTeacher
var ChemTeacher = function(name, id, salary, bonus) {
EngTeacher.call(this, name, id, salary);
this.bonus = bonus;
}
// Inherit the SchoolStaff prototype
ChemTeacher.prototype = Object.create(EngTeacher.prototype);
// Set the engTeacher constructor to engTeacher object
ChemTeacher.prototype.constructor = ChemTeacher;
// method on engTeacher object
ChemTeacher.prototype.print = function() {
console.log("Name : " + this.name + " Salary : " + this.salary + " Employee id: " + this.id + " bonus : " + this.bonus);
}
// create new objcts and check sub class have base class methods access
var schoolStaff = new SchoolStaff("Base Class", 100);
console.log(schoolStaff.sayHello()); // Hello Mr : Base Class
var engTeacher = new EngTeacher("Eng Teacher", 1001, 20000);
engTeacherPara.innerHTML = engTeacher.sayHello(); // Hello Mr : Eng Teacher
var chemTeacher = new ChemTeacher("Chem Teacher", 1001, 30000, 4000);
chemTeacherPara.innerHTML = chemTeacher.sayHello(); // Hello Mr : Chem Teacher
})();