JavaScript 原型和原型链
对象可以分为函数对象和普通对象,prototype是函数对象才有的属性,__proto__是每个对象都有的一个属性,
通过new Function 的是函数对象,
function f() {} ==> var f = new Function(); 两者是等价的
var o ={}; //普通对象
console.log(typeof o) //object
var o2 = new f(); //函数实例是普通对象
console.log(typeof o2) //object
重点:
1、无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个 constructor构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。People.prototype. constructor 指向 People。
2、当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性__proto__),指向构造函数的原型对象( People.prototype) 。
3、每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,
则继续搜索指针指向的原型对象
![](https://img2018.cnblogs.com/blog/1694613/201907/1694613-20190705094441026-425875007.png)
原型(prototype)检测
上面提到只有函数对象才有prototype,现在来检测一下
1 var o3 ={}; //o3 = new Object() 2 console.log(o3.prototype); //undefined 3 console.log(o3 instanceof Object); //实例o3在不在Object构造函数中 true 4 5 function Demo() {} //函数对象 6 var d = new Demo(); //普通对象 7 console.log(d.prototype); //undefined 8 console.log(d instanceof Demo); 9 console.log(typeof Demo); 10 console.log(Demo.prototype); //{constructor: ƒ} 函数对象确实存在原型
原型对象
1 function Cat(name,color){ //构造函数 2 this.name = name; 3 this.color = color; 4 // this.type = '动物'; 5 // this.eat = function() { 6 // console.log('吃老鼠') 7 // } 8 }; 9 Cat.prototype.type = '动物'; 10 Cat.prototype.eat = function() { 11 console.log('吃老鼠') 12 } 13 var cat1 = new Cat('大明','黑色'); 14 var cat2 = new Cat('小明','白色');
函数对象对原型属性的检测
1 //in 无论是自身的还是原型属性都返回true 2 console.log('name' in cat1) ;//true 3 console.log('type' in cat1) ;//true 4 //hasOwnProperty() 自身的属性返回true 原型属性返回false 5 console.log(cat1.hasOwnProperty('name')); //true 6 console.log(cat1.hasOwnProperty('type')); //false
继承
apply() 与 call()
apply() 与 call()是函数对象的两个方法,在一个函数对象中调用另一个函数对象 call(this,参数1,参数2) apply(this,[参数1,参数3]),改变其作用域绑定到当前作用域。
两者区别: 就是参数不同, apply 传数组 call 一个个传;
1 function Animal(){ //一个对象 父对象 2 this.type = '动物' 3 }; 4 function Cat(name,color){ //另一个对象 子对象 5 Animal.apply(this); //Animal在当前Cat对象中执行,将父对象的构造函数绑定到子对象上 6 this.name = name; 7 this.color = color; 8 }; 9 var cat1 = new Cat('大毛','黄色'); 10 cat1.type; //'动物'
直接继承
1)让Cat的原型直接继承Animal的实例
function Animal(){ //一个对象 父对象 this.type = '动物' }; function Cat(name,color){ //另一个对象 子对象 this.name = name; this.color = color; }; Cat.prototype = new Animal(); var cat1 = new Cat('大毛','黄色'); cat1.type; //'动物'
2)让Cat的原型直接继承Animal的原型
有个缺点就是Cat.prototype和Animal.prototyper指向同一个对象,指向同一块内存地址,当修改Cat.prototype的值时,Animal.prototype也会变化
function Animal(){}; //一个对象 父对象 Animal.prototype.type = '动物'; function Cat(name,color){ //另一个对象 子对象 this.name = name; this.color = color; }; Cat.prototype = Animal.prototype; Cat.prototype.abc = 'abc'; var cat1 = new Cat('大毛','黄色'); cat1.type; //'动物' console.log(Animal.prototype);//{type: "动物", abc: "abc", constructor: ƒ}
原型属性的CURD操作
原型继承说白了就是通过__proto__链接起来的
Cat.prototype 继承 的是 Animal实例对象 上面说到不管是函数对象还是普通对象它都有__proto__这个属性,原本cat1实例对象的__proto__指向的是 Cat函数对象的prototype对象,
但是现在Cat.prototype = new Animal(); 所以此时的cat1实例对象的__proto__指向的是 new Animal()实例 ===> 等价于 cat1.__proto__ = new Animal(); 通过cat1.__proto__可以获取Animal信息
function Animal(){ this.type= '动物'; }; function Cat(name,color){ //构造函数 this.name = name; this.color = color; }; Cat.prototype = new Animal(); var cat1 = new Cat('大明','黑色'); var cat2 = new Cat('小明','白色'); console.log(cat1.type); //动物 //获取属性 console.log(cat1.__proto__.type='猫科动物'); //猫科动物 //设置属性 cat1.__proto__.action="敏捷"; //删除属性 delete cat1.__proto__.type; //查看属性 console.log(cat1.__proto__.action);//敏捷 //查看继承的整个实例对象内容 console.log(cat1.__proto__); //Animal {type: "猫科动物"}
另一种写法
function Animal(){ this.type= '动物'; }; Animal.prototype.type2='动物2'; function Cat(name,color){ //构造函数 this.name = name; this.color = color; }; Cat.prototype = new Animal(); var cat1 = new Cat('大明','黑色'); var cat2 = new Cat('小明','白色'); // cat1.type; console.log(cat1.__proto__.__proto__.type2='abc'); //修改 打印出abc delete cat1.__proto__.__proto__.type2; //删除 console.log(cat1.type2); //已经删除,打印出undefined
cat1.__proto__指向的是 Animal的实例 如果需要获得Animal原型中的属性就需要再加上一个__proto__ 中间用点连接,其实这就是原型链
原型链
f 实例 指向 F3的原型,此时F3原型存放着F2实例的属性, F3的原型又指向F2原型,F2原型对象里面存放着F1实例的值, 然后F2原型指向 F1原型.最终都会指向Object原型
function F1(){ this.name1 = 'f1'; }; function F2(){ this.name2 = 'f2'; }; function F3(){ this.name3 = 'f3'; }; F2.prototype = new F1(); F3.prototype = new F2(); var f = new F3(); console.log(f.name3); //f3