JS中原型链,说简单也简单。
首先明确:
首先,我画了一张图。
所谓原型链,指的就是图中的proto这一条指针链!
DEMO:
var Person=function(){};
var p=new Person();
var t=new Person();
p.__proto__.say=function(){
console.log(1);
}
p.say(); //1
t.say(); //1
p.__proto__={
say:function(){
console.log(2);
}
}
p.say(); //2
t.say(); //1
原型链的顶层就是Object.prototype,而这个对象的是没有原型对象的。
可在chrome的控制台里面输入:
Object.__proto__
输出是:
function Empty() {}
原型链,如此而已。
对于初学者来说,JavaScript的原型是一个很让人头疼的事情,一来prototype容易与__proto__混淆,二来它们之间的各种指向实在有些复杂,其实市面上已经有非常多的文章在尝试说清楚,有一张所谓很经典的图,上面画了各种线条,一会连接这个一会连接那个,说实话我自己看得就非常头晕,更谈不上完全理解了。所以我自己也想尝试一下,看看能不能把原型中的重要知识点拆分出来,用最简单的图表形式说清楚。
我们知道原型是一个对象,其他对象可以通过它实现属性继承。但是尼玛除了prototype,又有一个__proto__是用来干嘛的?长那么像,让人怎么区分呢?它们都指向谁,那么混乱怎么记啊?原型链又是什么鬼?相信不少初学者甚至有一定经验的老鸟都不一定能完全说清楚,下面用三张简单的图,配合一些示例代码来理解一下。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript">
function JS(x, y) {
this.x = x;
this.y = y;
this.fun = function() {
console.log(this.x + "---" + this.y);
}
}
function Node(x, y) {
JS.call(this, x, y); //继承
this.fun = function() {
console.log(this.x + "***" + this.y);
}
}
var a = new JS("ES5", "ES6");
var b = new Node("Express", "Koa2");
console.log(Object.prototype==JS.prototype.__proto__); //true
console.log(a.constructor==JS); //true
console.log(a.__proto__==JS.prototype); //true
console.log(a.prototype==undefined); //true 只有函数才有prototype,对象没有
console.log(JS.prototype.__proto__==Object.prototype); //true
console.log(JS.prototype.constructor==JS); //true
console.log(JS.prototype.__proto__.__proto__==null) //true
console.log(Object.prototype.__proto__==null); //true
</script>
</head>
<body>
</body>
</html>
var a = {};
console.log(a.prototype); //undefined
console.log(a.__proto__); //Object {}
var b = function(){}
console.log(b.prototype); //b {}
console.log(b.__proto__); //function() {}
/*1、字面量方式*/
var a = {};
console.log(a.__proto__); //Object {}
console.log(a.__proto__ === a.constructor.prototype); //true
/*2、构造器方式*/
var A = function(){};
var a = new A();
console.log(a.__proto__); //A {}
console.log(a.__proto__ === a.constructor.prototype); //true
/*3、Object.create()方式*/
var a1 = {a:1}
var a2 = Object.create(a1);
console.log(a2.__proto__); //Object {a: 1}
console.log(a.__proto__ === a.constructor.prototype); //false(此处即为图1中的例外情况)
var A = function(){};
var a = new A();
console.log(a.__proto__); //A {}(即构造器function A 的原型对象)
console.log(a.__proto__.__proto__); //Object {}(即构造器function Object 的原型对象)
console.log(a.__proto__.__proto__.__proto__); //null
图解构造器Function和Object的关系:
Function instanceof Object;//true
Object instanceof Function;//true
这个是怎么一回事呢?要从运算符instanceof说起。
我曾经简单理解instanceof只是检测一个对象是否是另个对象new出来的实例(例如var a = new Object(),a instanceof Object返回true),但实际instanceof的运算规则上比这个更复杂。
首先w3c上有官方解释(传送门,有兴趣的同学可以去看看),但是一如既往地让人无法一目了然地看懂……
知乎上有同学把这个解释翻译成人能读懂的语言(传送门),看起来似乎明白一些了:
//假设instanceof运算符左边是L,右边是R
L instanceof R
//instanceof运算时,通过判断L的原型链上是否存在R.prototype
L.__proto__.__proto__ ..... === R.prototype ?
//如果存在返回true 否则返回false
注意:instanceof运算时会递归查找L的原型链,即L.__proto__.__proto__.__proto__.__proto__...直到找到了或者找到顶层为止。
所以一句话理解instanceof的运算规则为:
instanceof检测左侧的__proto__原型链上,是否存在右侧的prototype原型。
我们再配合代码来看一下就明白了:
//①构造器Function的构造器是它自身
Function.constructor=== Function;//true
//②构造器Object的构造器是Function(由此可知所有构造器的constructor都指向Function)
Object.constructor === Function;//true
//③构造器Function的__proto__是一个特殊的匿名函数function() {}
console.log(Function.__proto__);//function() {}
//④这个特殊的匿名函数的__proto__指向Object的prototype原型。
Function.__proto__.__proto__ === Object.prototype//true
//⑤Object的__proto__指向Function的prototype,也就是上面③中所述的特殊匿名函数
Object.__proto__ === Function.prototype;//true
Function.prototype === Function.__proto__;//true
当构造器Object和Function遇到instanceof
我们回过头来看第一部分那个“奇怪的现象”,从上面那个图中我们可以看到:
Function.__proto__.__proto__ === Object.prototype;//true
Object.__proto__ === Function.prototype;//true
所以再看回第一点中我们说的instanceof的运算规则,Function instanceof Object 和 Object instanceof Function运算的结果当然都是true啦!
如果看完以上,你还觉得上面的关系看晕了的话,只需要记住下面两个最重要的关系,其他关系就可以推导出来了:
1、所有的构造器的constructor都指向Function
2、Function的prototype指向一个特殊匿名函数,而这个特殊匿名函数的__proto__指向Object.prototype
完整DEMO:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript">
function JS(x, y) {
this.x = x;
this.y = y;
this.fun = function() {
console.log(this.x + "---" + this.y);
}
}
function Node(x, y) {
JS.call(this, x, y); //继承
this.fun = function() {
console.log(this.x + "***" + this.y);
}
}
var a = new JS("ES5", "ES6");
var b = new Node("Express", "Koa2");
console.log(Object.prototype == JS.prototype.__proto__); //true
console.log(a.constructor == JS); //true
console.log(a.__proto__ == JS.prototype); //true
console.log(a.prototype == undefined); //true 只有函数才有prototype,对象没有
console.log(JS.prototype.__proto__ == Object.prototype); //true
console.log(JS.prototype.constructor == JS); //true
console.log(JS.prototype.__proto__.__proto__ == null) //true
console.log(Object.prototype.__proto__ == null); //true
console.log("----------------------------------------");
function Fun() {}
//我创造了一个函数Fn
//这个函数由Function生成(Function作为构造函数)
var fn = new Fun()
//我创建了一个函数fn
//这个函数由Fn生成(Fn作为构造函数)
console.log(fn.__proto__ === Fun.prototype) //true
//fn的__proto__指向其构造函数Fun的prototype
console.log(Fun.__proto__ === Function.prototype) //true
//Fun的__proto__指向其构造函数Function的prototype
console.log(Function.__proto__ === Function.prototype) //true
//Function的__proto__指向其构造函数Function的prototype
//构造函数自身是一个函数,他是被自身构造的
console.log(Function.prototype.__proto__ === Object.prototype) //true
//Function.prototype的__proto__指向其构造函数Object的prototype
//Function.prototype是一个对象,同样是一个方法,方法是函数,所以它必须有自己的构造函数也就是Object
console.log(Fun.prototype.__proto__ === Object.prototype) //true
//与上条相同
//此处可以知道一点,所有构造函数的的prototype方法的__都指向__Object.prototype(除了....Object.prototype自身)
console.log(Object.__proto__ === Function.prototype) //true
//Object作为一个构造函数(是一个函数对象!!函数对象!!),所以他的__proto__指向Function.prototype
console.log(Object.prototype.__proto__ === null) //true
//Object.prototype作为一切的源头,他的__proto__是null
//下面是一个新的,额外的例子
var obj = {}
//创建了一个obj
console.log(obj.__proto__ === Object.prototype) //true
//obj作为一个直接以字面量创建的对象,所以obj__proto__直接指向了Object.prototype,而不需要经过Function了!!
//下面是根据原型链延伸的内容
//还有一个上文并未提到的constructor, constructor在原型链中,是作为对象prototypr的一个属性存在的,它指向构造函数(由于主要讲原型链,这个就没在意、);
console.log(obj.__proto__.__proto__ === null) //true
console.log(obj.__proto__.constructor === Object) //true
console.log(obj.__proto__.constructor.__proto__ === Function.prototype) //true
console.log(obj.__proto__.constructor.__proto__.__proto__ === Object.prototype) //true
console.log(obj.__proto__.constructor.__proto__.__proto__.__proto__ === null) //true
console.log(obj.__proto__.constructor.__proto__.__proto__.constructor.__proto__ === Function.prototype) //true
//以上,有兴趣的可以一一验证 F12搞起.
</script>
</head>
<body>
</body>
</html>
js中的hasOwnProperty()和isPrototypeOf()
这两个属性都是
Object.prototype
所提供:Object.prototype.hasOwnProperty()
和Object.prototype.isPropertyOf()
先讲解hasOwnProperty()
方法和使用。在讲解isPropertyOf()
方法和使用
看懂这些至少要懂原型链
一、Object.prototype.hasOwnProperty()
概述
hasOwnProperty()方法用来判断某个对象是否含有指定的自身属性
语法
obj.hasOwnProperty("属性名");//实例obj是否包含有圆括号中的属性,是则返回true,否则是false
描述
所有继承了Object.prototype
的对象都会从原型链上继承到hasOwnProperty
方法,这个方法检测一个对象是否包含一个特定的属性,
和in
不同,这个方法会忽略那些从
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript">
function A() {
this.a = "张三";
this.b = "李四";
}
function B() {
A.call(this);
this.c = "王五";
this.d = "赵六";
}
var a1=new A();
var b1=new B();
console.log(b1.hasOwnProperty("a")); //true
console.log(b1.hasOwnProperty("b")); //true
console.log(b1.hasOwnProperty("c")); //true
console.log(b1.hasOwnProperty("d")); //true
</script>
</head>
<body>
</body>
</html>
实例1.使用hasOwnProperty()方法判断某对象是否含有特定的自身属性
下面的例子检测了对象 o 是否含有自身属性 prop:
var o =new Object();
o.prop="exists";
function change(){
o.newprop=o.prop;
delete o.prop;
}
o.hasOwnProperty("prop")//true
change()//删除o的prop属性
o.hasOwnProperty("prop")//false
//删除后在使用hasOwnProperty()来判断是否存在,返回已不存在了
2.自身属性和继承属性的区别
下面的列子演示了hasOwnProperty()
方法对待自身属性和继承属性的区别。
var o =new Object();
o.prop="exists";
o.hasOwnProperty("prop");//true 自身的属性
o.hasOwnProperty("toString");//false 继承自Object原型上的方法
o.hasOwnProperty("hasOwnProperty");//false 继承自Object原型上的方法
3.修改原型链后hasOwnProperty()的指向例子
下面的列子演示了hasOwnProperty()
方法对待修改原型链后继承属性的区别
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript">
var o = {
name: 'jim'
};
function Person() {
this.age = 19;
}
Person.prototype = o; //修改Person的原型指向
var p = new Person();
console.log(p.hasOwnProperty("name")); //false 无法判断继承的name属性
console.log(p.hasOwnProperty("age")); //true;
</script>
</head>
<body>
</body>
</html>
4.使用hasOwnProperty()遍历一个对象自身的属性
下面的列子演示了如何在遍历一个对象忽略掉继承属性,而得到自身属性。
注意· forin
会遍历出对象继承中的可枚举属性
var o={
gender:'男'
}
function Person(){
this.name="张三";
this.age=19;
}
Person.prototype=o;
var p =new Person();
for(var k in p){
if(p.hasOwnProperty(k)){
console.log("自身属性:"+k);// name ,age
}else{
console.log("继承别处的属性:"+k);// gender
}
}
5.hasOwnProperty方法有可能会被覆盖
如果一个对象上拥有自己的hasOwnProperty()
方法,则原型链上的hasOwnProperty()
的方法会被覆盖掉
var o={
gender:'男',
hasOwnProperty:function(){
return false;
}
}
o.hasOwnProperty("gender");//不关写什么都会返回false
//解决方式,利用call方法
({}).hasOwnProperty.call(o,'gender');//true
Object.prototype.hasOwnProperty.call(o,'gender');//true
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript">
var o = {
o1: {
o2: {
name: "SB JS"
}
}
};
console.log(o.hasOwnProperty("o1")); //true
console.log(o.hasOwnProperty("o2")); //false
console.log(o.o1.hasOwnProperty("o2")); //true
console.log(o.o1.hasOwnProperty("name")); //false
console.log(o.o1.o2.hasOwnProperty("name")); //true
</script>
</head>
<body>
</body>
</html>
二、Object.prototype.isPrototypeOf()
概述
isPrototypeOf()
方法测试一个对象是否存在另一个对象的原型链上
语法
//object1是不是Object2的原型,也就是说Object2是Object1的原型,,是则返回true,否则false
object1.isPrototypeOf(Object2);
描述
isPrototypeOf()
方法允许你检查一个对像是否存在另一个对象的原型链上
实例1.利用isPrototypeOf()检查一个对象是否存在另一个对象的原型上
var o={};
function Person(){};
var p1 =new Person();//继承自原来的原型,但是现在已经无法访问
Person.prototype=o;
var p2 =new Person();//继承自o
console.log(o.isPrototypeOf(p1));//false o是不是p1的原型
console.log(o.isPrototypeof(p2));//true o是不是p2的原型
2.利用isPropertyOf()检查一个对象是否存在一另一个对象的原型链上
var o={};
function Person(){};
var p1 =new Person();//继承自原来的原型,但是现在已经无法访问
Person.prototype=o;
var p2 =new Person();//继承自o
console.log(o.isPrototypeOf(p1));//false o是不是p1的原型
console.log(o.isPrototypeof(p2));//true o是不是p2的原型
console.log(Object.prototype.isPrototypeOf(p1));//true
console.log(Object.prototype.isPrototypeOf(p2));//true
p1
的原型链结构是p1
=>原来的Person.prototype
=>Object.prototype
=>null
p2
的原型链结构是p2
=> o
=>Object.prototype
=>null
p1
和p2
都拥有Object.prototype
所以他们都在Object.Prototype
的原型链上
三、总结
- hasOwnProperty:是用来判断一个对象是否有你给出名称的属性或对象。不过需要注意的是,此方法无法检查该对象的原型链中是否具有该属性,该属性必须是对象本身的一个成员。
- isPrototypeOf是用来判断要检查其原型链的对象是否存在于指定对象实例中,是则返回true,否则返回false。