原型与原型链
JavaScript万物皆由对象,一切皆空!!!
1.设计模式
1.1面向对象
-
特征:继承、封装、多态
-
java和js继承的区别:
继承:继承属于类与类之间的关系,继承的目的是为了实现数据共享;
js:是一种基于原型的面向对象的编程语言,js通过构造函数模拟类,通过原型来实现继承;
java:是一种面向对象的编程语言,类实现继承;
2.原型链和原型
2.1理论
prototype、_proto_
- prototype(显式原型):所有的函数的一个属性prototype,这个属性指向函数的原型对象;
_proto_
(隐式原型):每个对象都有一个__proto__
属性,指向该实例对象对应的原型对象;- constructor:原型对象有一个constructor属性,指向该原型对象对应的构造函数
构造函数、实例、原型对象
-
构造函数:用来创建对象的函数;
-
实例:实例就是通过构造函数创建出来的对象;
-
原型:每个函数在被创建的时候,都会默认有一个prototype属性,prototype的属性值是一个对象,也就是构造函数的原型;
/**
①写一个函数:
*/
function Bird(kind,color){
this.kind=kind;
this.color=color;
};
console.log(Bird.prototype,'原型对象');//原型对象
console.log(Bird.prototype.constructor,'指向函数');//指向函数
/**
②构建一个实例:
*/
let a=new Bird('猫头鹰','灰色');
console.log(a,'实例对象');//实例对象
console.log(a.__proto__,'指向原型对象');//指向原型对象
console.log(a.__proto__.constructor,'指向函数');//指向函数
/**
③构造函数、原型、实例
*/
console.log(a.__proto__===Bird.prototype);
console.log(a.__proto__.constructor===Bird.prototype.constructor);
- 关系图:
2.2找原型链
2.2.1数据类型
数组
/**
创建数组实例
*/
let arr=new Array(1,8,0);
console.log(arr);
console.log(Array.prototype,'构造函数的原型对象');
//①查看实例arr的原型
console.log(arr.__proto__,'原型');
console.log(arr.__proto__.constructor,'原型的指向');//Array
console.log(arr.__proto__===Array.prototype);
//②查看实例arr的原型的原型
console.log(arr.__proto__.__proto__,'原型的原型');
console.log(arr.__proto__.__proto__.constructor,'原型的原型的指向');//Object
console.log(arr.__proto__.__proto__===Object.prototype);
//③原型链的终点
console.log(arr.__proto__.__proto__.__proto__,'终点');
字符串
/**
创建字符串实例
*/
let str=new String('43');
console.log(str);
console.log(String.prototype,'构造函数的原型对象');
//①查看实例str的原型
console.log(str.__proto__,'原型');
console.log(str.__proto__.constructor,'原型的指向');
console.log(str.__proto__===String.prototype);//String
//②查看实例str的原型的原型
console.log(str.__proto__.__proto__,'原型的原型');
console.log(str.__proto__.__proto__.constructor,'原型的原型的指向');//Object
console.log(str.__proto__.__proto__===Object.prototype);
//③原型链的终点
console.log(str.__proto__.__proto__.__proto__,'终点');
DOM
<body>
<div>div标签</div>
<p>P标签</p>
<a href="#">a标签</a>
<script>
let div = document.querySelector('div');
let p = document.querySelector('p');
let a = document.querySelector('a');
//①找原型
console.log(div.__proto__, 'div第一层的原型');
console.log(p.__proto__, 'p的原型');
console.log(a.__proto__, 'a的原型');
//②原型的指向
console.log(div.__proto__.constructor, 'div的原型指向');
console.log(p.__proto__.constructor, 'p的原型指向');
console.log(a.__proto__.constructor, 'a的原型指向');
//③原型的原型
console.log(div.__proto__.__proto__, 'div第二层原型');
console.log(p.__proto__.__proto__, 'p的原型的原型');
console.log(a.__proto__.__proto__, 'a的原型的原型');
//④原型的原型的指向
console.log(div.__proto__.__proto__.constructor, 'div的原型的原型指向');
//⑤原型的原型的原型
console.log(div.__proto__.__proto__.__proto__, 'div第三层原型');
console.log(div.__proto__.__proto__.__proto__.__proto__, 'div第四层原型');
console.log(div.__proto__.__proto__.__proto__.__proto__.__proto__, 'div第五层原型');
console.log(div.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__, 'div第六层原型');
console.log(div.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__, 'div第七层原型');
</script>
</body>
2.2.2 instanceof
语法:object instanceof constructor
作用:用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
/**
①字符串
*/
let a='abc';
let b=new String('abc');
console.log(a==b)//true;
console.log(a===b)//false;
console.log(b instanceof Object);
console.log(a instanceof Object);
console.log(a instanceof String);
console.log(b instanceof String);
/**
②数组
*/
let a=[1,8,0];
let b=new Array(1,8,0);
console.log(b instanceof Object);
console.log(a instanceof Object);
console.log(b instanceof Array);
console.log(a instanceof Array);
/**
②null和undefined
*/
let a=null;
let b=undefined;
console.log(b instanceof Object);
console.log(a instanceof Object);
/**
ECMAScript 规范认为(https://262.ecma-international.org/5.1/#sec-4.3.9)
If x is null and y is undefined, return true.
If x is undefined and y is null, return true.
*/
console.log(a==b);//true
ToObject原则(补充)
JS中使用字符串可以直接赋值字符串字面量,或者是new String("252")
那这两中定义字符串的区别在哪?
当我们尝试访问一个primitive值的属性时,JS引擎内部会调用一个内置[[toObject]] 方法,将字面量的”252”转为一个[[PrimitiveValue]]为”252”的String对象,然后从其原型链中尝试查找需要访问的属性,使用结束后再释放掉这个String对象。
let a='22';
let b=new String('22');
console.log(a);
console.log(b);
简而言之就是把构造函数创建的字符串通过ToObject算法转换成了字面量定义的值。
ECMAScript 规范规范:https://tc39.es/ecma262/
题目应用
function Box() { this.name = '我是Box'; }
function Desk() { this.age = 100; }
Desk.prototype = new Box();//通过原型链继承
var desk = new Desk();
console.log(desk instanceof Box);//true
console.log(Desk.prototype instanceof Box);//true
console.log(Desk.prototype instanceof Desk);//false
3总结
原型继承
-
概念:把父对象作为子对象构造函数的原型;
-
代码演示:
//父对象 var parent={ home:{ plate:'150号', stree:'香榭丽舍大道' }, money:1000000 } //子对象 function Son(name,age){ this.name=name; this.age=age; } var result=new Son('zzh',18); console.log(result,'没继承parent时候的实例对象'); /** 原型继承 */ Son.prototype=parent; Son.prototype.construct=Son;//可选 var result=new Son('zzh',18); //Son.prototype.constructor=Son;//可选 console.log(result,'已经继承parent属性的实例对象'); console.log(Son.prototype.constructor);
访问原型链的规则
就近原则:对象先访问自己的属性,自己没有就找原型的,原型没有就找原型的原型,一直到原型链终点null,如果还找不到,属性则获取undefined。
function Person(name,age){
this.name=name;
this.age=age
};
Person.prototype.country='中国';
Person.prototype.eat=function(){
console.log('吃零食')
}
let p1=new Person('张三',18);
/**
得到的结果
*/
console.log(p1.name);
console.log(p1.age);
console.log(p1.country);
console.log(p1.height);
p1.eat();
p1.learn();
5问题
-
原型是什么?
每个函数都有一个属性prototype(显式原型),这个属性指向函数的原型对象;每个对象都有一个属性
_proto_
(隐式原型),这个属性指向该实例对象对应的原型对象; -
原型链是什么?原型链的作用?
JS中万物皆对象,对象与对象之间有关联,当我们寻找一个对象的属性的时候,会先去找它自己的属性有没有这个属性,如果自己没有,就会顺着它的原型属性上面找,看有没有这个属性,如果没有,则继续找下去,这个关系就形成了一条链式,专业术语叫原型链,而原型链是JS用来实现继承的一种方式;
-
为什么[] instanceof Object会等于true?
因为instanceof就是在原型链上面找有没有出现过,如果出现过为true,反之false,而在数组的原型链中,第二层原型链中出现过对象构造函数,所以[] instanceof Object等于true;
-
原型链访问原则是什么?终点是什么?
原型链的访问原则就是,先去找对象本来的属性,如果没有出现再去找原型上面的属性,如果还是没有找到,那就去找原型的原型属性,最后找到null的时候,原型链访问结束;原型链的终点是null;