javascript之原型 发表于 2018-08-12 prototype 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192var fn = function() {}console.log(typeof fn); // functionconsole.log(fn instanceof Object); // true// 函数确实是对象function Person(name) { this.name = name;}var person = new Person('dottie');console.log(person) // Person { name: 'dottie' }// 故,对象都是通过函数来创建的。// 但是, 又有问题了。 下面创建的方式我们没用函数var person2 = { name: 'dottie'};console.log(person2); // { name: 'dottie' }// 其实。这种方式是这样创建对象的。var person3 = new Object();person3.name = 'dottie'console.log(person3); // { name: 'dottie' }console.log(Object.prototype.toString.call(person2)); // [object Object]// 总之,对象都是通过函数来创建的。// 上面的function Person() {}// 这里的Person就是构造函数。console.log(person.constructor === Person); // true// 那么person的constructor属性是哪儿来的呢?// 这里就扯出了 prototype 原型的话题了。// 每一个对象都有一个 __proto__ 属性 指向该对象函数的prototypeconsole.log(person.__proto__ === Person.prototype); // true// 而prototype上面就是挂载了许多东西:// 1. 自身方法 如 Object.prototype.toString方法// 2. constructor 方法// 3. __proto__ 因为万事万物皆为对象,这里的对象函数Person(){} 其实他也是一个对象,是Function的实例对象,console.log(Person.__proto__ === Function.prototype) // true// 并且 Person.prototype 其实也是一个对象,他的 __proto__是指向Object.prototypeconsole.log(Person.prototype.__proto__ === Object.prototype) // true// 所以,当我们调用person.toString()方法时,我们Person类并没有定义toString方法。但是却可以直接使用toString方法。// 是因为person对象中有一条原型链。当在Person类中没有我们需要的方法时,就会顺着原型链向上找,直到直到Object中prototype的方法。// 而又有人会好奇,既然万事万物都是对象,那么Object.prototype的__proto__指向哪儿呢。答案是null。 否则就会造成一个死循环。// Object.__proto__为Function.prototypeconsole.log(Object.__proto__ === Function.prototype); // true// 所以对象的原型链如下; // 调用person.toString方法的流程:// 1. 先在Person中toString方法,// 2. 没找到。去 person.__proto__中也就是 Person.prototype中去找toString方法,// 3. 没找到. 继续去 Person.prototype.__proto__中也就是Object.prototype中去找,// 4. 最终在 Object.prototype中找到了toString方法了。// ------------------------------// 还要有人会好奇。当我们对函数调用call或者apply方法时。那么为什么函数可以调用call和apply方法?// var aa = function() {alert(this)}// aa.call(); 就是将当前context的this。传递给aa函数。// 那么call方法时哪儿来的呢?肯定不是Object.prototype的。 // 其实call和apply方法时Function.prototype的方法。// 验证var aaa = function() {}console.log(aaa.apply === Object.prototype.apply) // falseconsole.log(aaa.apply === Function.prototype.apply) // truevar names = Object.getOwnPropertyNames(Function) // [ 'length', 'name', 'prototype' ]var allNames = Object.getOwnPropertyNames(Function.prototype)console.log(allNames); // 遍历 [ 'length','name','arguments','caller','constructor','apply','bind','call','toString' ]console.log(Function.name === Function.prototype.name) // falseconsole.log(new aaa().constructor === aaa.prototype.constructor) // trueconsole.log(aaa.constructor === aaa.prototype.constructor) // falseconsole.log(aaa.constructor === Function.prototype.constructor) // trueconsole.log(aaa.constructor) // [Function: Function]console.log(aaa.prototype.constructor) // [Function: aaa]console.log(person.toString()) // [object Object]// 我们修改Object中toString方法。适用于我们person类输出。// Object.prototype.toString = function() {// return this.name;// };// console.log(person.toString()) // dottie// 或者我们自己定义toString方法Person.prototype.toString = function() { return this.name;};console.log(person.toString()) // dottie