回想本身已经工做了有一段时间了,可是本身对JavaScript的原型链、和继承的理解能力没有到位,最近他们完全的整理而且复习了一遍。javascript
本案例中部分文案来自网络和书籍,若有侵权请联系我,我只是把个人理解和想法告诉你们。java
本着互联网分享精神、如今我就将个人学习以及理解分享给你们。若是那里又说的不对的地方请私信我,我会及时回复。c++
讲解:不少人见到了久违的new操做符,因而就叫Person为“类”,但是又没有关键字class的出现,以为叫“类”有点勉强。因而退而求其次叫Person为类的构造函数。这些概念好像都没有错,之因此出现这样的状况,多是由于你们都学习了传统的面向对象语言(c++,c#,Java等),还有一种思惟定势吧。为了让javascript也面向对象,要在javascript中找到与传统面向对象语言的影子。但是按照javascript的说法,function定义的这个Person就是一个Object(对象),并且仍是一个很特殊的对象,这个使用function定义的对象与使用new操做符生成的对象之间有一个重要的区别。这个区别就是function定义的对象有一个prototype属性,使用new生成的对象就没有这个prototype属性。c#
function Person(name) { this.name = name; this.showMe = function () { console.log(this.name); } }; var per= new Person('铅笔') //实例化这个构造函数 per.showMe() //执行函数内部的方法
讲解:在上面的案例中已经说到了,function定义的函数是一个特殊对象,他拥有prototype属性。prototype属性能够在对象的原型中添加原型方法。网络
请看代码函数
function Person(name) { this.name = name; this.showMe = function () { console.log(this.name); } }; Person.prototype.user = function (user) { // 向原型中添加原型方法 return user }; Person.prototype.age = 23; // 像原型中添加属性,而且赋值, var one = new Person('js'); // 实例化方法。 console.log(one.user('这是原型对象')); //调用实例化方法的方法 console.log(one.age); //调用实例化方法的属性
案例二的代码中,为何能够调用Person的pertotype方法呢。这就要讲到Peron在实例化时候发生的事情。学习
第一件事情:创建了一个one对象。(能够理解为 var one={})this
第二件事情:将one对象的内置原型对象(__proto__)设置为构造函数person的prototype属性所引用的那个原型对象。(能够理解为:one.__proto__ = Person.prototype; 这个__proto__稍后再讲)spa
第三件事:将one对象做为this参数调用构造函数person(能够理解为var one={ Person.call(this}; 也就至关于当前对象拥有了Person中全部的属性和方法)prototype
这样就能够理解为何one能够直接调用proson的pertotype方法了。
在prototype对象中又有一个constructor属性,这个constructor属性一样指向一个constructor对象,而这个constructor对象偏偏就是这个function函数自己(Person)。
function Person(name) { this.name = name; this.showMe = function () { alert(this.name); } }; var one = new Person('js'); alert(one.prototype)//undefined alert(typeof Person.prototype);//object alert(Person.prototype.constructor);//function Person(name) {...};
以上三案例是否是看着有点晕了,其实没那么复杂,直接上图。
在案列二中讲到每个函数都有一个protytype属性,而对象却没有,可是对象有一个__propt__内置属性,而且这个属性是包含对指定对象的内部原型的引用。原型链上的对象正是依靠这个__proto__属性连结在一块儿的。注意函数本质也是对象并且是特殊对象,因此函数也有__propt__属性。
function Person(name) { this.name = name; this.showMe = function () { console.log(this.name); } }; Person.prototype.from = function () { console.log('I come from prototype.'); } var per = new Person('js'); console.log(per.__proto__) //输出 Person { from: [Function] }
在举一个原型链的例子
var Person = function() {}; Person.prototype.say = function() { console.log("Person say"); }; Person.prototype.salary = 50000; var Programmer = function() {}; Programmer.prototype = new Person(); Programmer.prototype.writeCode = function() { console.log("Programmer writes code"); }; var p = new Programmer(); p.say(); // Person say p.writeCode(); // Programmer writes code console.log(p.salary); // 50000 console.log(p.__proto__) // Person { writeCode: [Function] } console.log(p.__proto__.__proto__) //Person { say: [Function], salary: 50000 } 看沒看到,这里才有salary和say方法 这就是原型链,直到找到这个方法为止。 console.log(p.__proto__.__proto__.__proto__) // {} __proto__会一直向上查找,直到{}才会中止,此刻就会证实没有找到方法或属性。到头了呀亲、
原型链上图。唉图画的好累啊。
在举一个修改__proto__指向的例子
function Person(name) { this.name = name; this.showMe = function () { console.log(this.name); } } Person.prototype.from = function () { console.log('I come from prototype.'); }; function SubPer() { this.tests = function () { console.log('tst') } } var son = new SubPer(); console.log(son.__proto__); //SubPer {} son.tests(); // son.from() //此刻会报错 son.__proto__ = new Person('name'); //修改son实例对象的prototype原型对象的引用 console.log(son.__proto__); // Person { name: undefined, showMe: [Function] } son.showMe() //name son.tests(); //此方法会被调用,由于SubPer函数内自身就有 tests 函数。 son.from(); //此刻会调用Person原型对象方法,由于更改了son实例对象的prototype原型对象的引用。
在JavaScript的对象中有这样一个神奇的方法hasOwnProperty,他能够判断该方法或属性是对象自身的仍是原型链上的,废话很少说直接上代码。
function a(name) { this.name = name; this.showName = function () { console.log(this.name) } } function b(name) { this.name = name; this.showName = function () { console.log(this.name) } } b.prototype.say = function () { return 'say' }; var cat = new b('123456'); cat.user = '铅笔'; cat.showName(); console.log(cat.hasOwnProperty('showName')); //true showName方法是构造函数b内的方法,cat对象是经过实例化b获得的。在实例化b的时候会有这么一步 b.call(cat,'123456'); 因此返回true console.log(cat.hasOwnProperty('say')); //false say属于cat内置原型对象(__propo__)指向原型链的方法,不算自身的方法, console.log(cat.hasOwnProperty('user')); //true 虽然属性是在new 后加的,可是也算是对象的属性呀,因此返回的ture
那么若是对象中包含haoOwnProperty怎么办。-终极方案
function a(name) { this.name = name; this.showName = function () { console.log(this.name) } } function b(name) { this.name = name; this.showName = function () { console.log(this.name) } } b.prototype.say = function () { return 'say' }; var cat = new b('123456'); cat.hasOwnProperty = function () { return true }; cat.user = '铅笔'; cat.showName(); console.log(cat.hasOwnProperty('啦啦啦')) // true console.log(({}).hasOwnProperty.call(cat, ('啦啦啦'))); // false 此刻只能采用终极方案,