文章目录
一.原型[prototype]
1.函数的prototype属性
-
并不是所有对象都能有prototype,只有函数才有prototype属性,每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
-
prototype也是一个对象,而且默认包含一个constructor属性,这个constructor属性指向函数自己
function Person(){ this.name='my name'; } var p=new Person(); //函数的prototype.constructor指向自己 console.log(Person.prototype.constructor===Person);//true
-
原型对象中有一个属性constructor, 它指向函数对象
constructor:当一个函数被用来构造一个对象时,那这个函数就被成为constructor
实例的构造函数属性(constructor)指向构造函数。
function Person(){ this.name='my name'; } var p=new Person(); //函数的prototype.constructor指向自己 console.log(Person.prototype.constructor===Person);//true //constructor:当一个函数被用来构造一个对象时,那这个函数就被成为constructor console.log(p.constructor); // ƒ Person(){ // this.name='my name'; // }
-
给原型对象添加属性(
一般都是方法
)作用: 函数的所有实例对象自动拥有原型中的属性(方法)
代码示例:
// 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
console.log(Date.prototype, typeof Date.prototype)
function Fun () { }
console.log(Fun.prototype) // 默认指向一个Object空对象(没有我们的属性)
// 原型对象中有一个属性constructor, 它指向函数对象
console.log(Date.prototype.constructor===Date)
console.log(Fun.prototype.constructor===Fun)
//给原型对象添加属性(一般是方法) ===>实例对象可以访问
Fun.prototype.test = function () { console.log('test()') }
var fun = new Fun()
fun.test()
2.显示原型与隐式原型
总结:只有函数才有prototype属性,而_*proto_*属性却是所有对象都有的。
-
每个构造函数function都有一个
prototype
,即显式
原型(属性) -
每个对象都有一个[
__ proto __
],可称为隐式
原型(属性) -
实例对象的隐式原型的值为其对应构造函数的显式原型的值
4.总结:
- 函数的[
prototype
]属性: 在定义函数时自动添加的, 默认值是一个空Object对象 - 对象的[
__ proto __
]属性: 创建对象时自动添加的,默认值为构造函数的prototype属性值
- 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)
5.代码示例:
//定义构造函数
function Fn() {
// 内部默认执行语句: this.prototype = {}
}
// 1. 每个函数function都有一个prototype,即显式原型属性, 默认指向一个空的Object对象
console.log(Fn.prototype)
// 2. 每个实例对象都有一个__proto__,可称为隐式原型
//创建实例对象
var fn = new Fn() // 内部默认执行语句: this.__proto__ = Fn.prototype
console.log(fn.__proto__)
// 3. 对象的隐式原型的值为其对应构造函数的显式原型的值
console.log(Fn.prototype===fn.__proto__) // true
//给原型添加方法
Fn.prototype.test = function () {
console.log('test()')
}
//通过实例调用原型的方法
fn.test()
3.constructor
构造函数的原型中包含属性 constructor
指向该构造函数
function User(name) {
this.name = name;
}
let p1 = new User("张三");
let ls = new p1.constructor("李四");
console.log(ls.name);//李四
以下代码直接设置了构造函数的原型将造成 constructor
丢失
function User(name) {
this.name = name;
}
User.prototype = {
//constructor: User,
show(){
console.log("hello");
}
};
let lisi=new User.prototype.constructor('李四')
let zhangsan=new User('张三')
zhangsan.show()//hello
console.log(lisi.constructor===zhangsan.constructor);//false
lisi.show()//报错:Uncaught TypeError: lisi.show is not a function
解释:
以上代码直接设置了构造函数的原型将造成 constructor
丢失
通过对象字面量给Person.prototype
进行赋值时,此时User的prototype发生了改变,User不再指向原来的prototype,而是指向了一个含有show()方法的新的prototype,但此时constructor丢失,新的User.prototype中没有constructor,所以李四会报错,
正确的做法是要保证原型中的 constructor
指向构造函数。
在设置User.prototype
时,加入代码constructor: User,
function User(name) {
this.name = name;
}
User.prototype = {
constructor: User,
show(){
console.log("hello");
}
};
let lisi=new User.prototype.constructor('李四')
let zhangsan=new User('张三')
zhangsan.show()//hello
console.log(lisi.constructor===zhangsan.constructor);//true
lisi.show()//hello
二.原型链
-
原型链:
访问一个对象的属性时,
先在自身属性中查找,找到返回
如果没有, 再沿着[__ proto __]这条链向上查找, 找到返回
如果最终没找到, 返回undefined
-
实例对象的隐式原型===构造函数的显式原型
-
函数的显示原型指向的对象默认是空Object实例对象(但Object不满足)
-
所有函数都是Function的实例(Function是它本身的实例)
自己是由自己创建的,好像不符合逻辑,但仔细想想,现实世界也有些类似,你是怎么来的,你妈生的,你妈怎么来的,你姥姥生的,……类人猿进化来的,那类人猿从哪来,一直追溯下去……,就是无,(NULL生万物)
正如《道德经》里所说“无,名天地之始”。
Function.prototype===Function.__proto__
5.Object的原型对象是原型链的尽头
function Fn(){
this.test1=function(){
console.log("test1()");
}
}
Fn.prototype.test2=function(){
console.log("test2()");
}
var fn=new Fn();
fn.test1();//test1()
fn.test2();//test2()
console.log(fn.toString());//[object Object]
console.log(fn.test3);//undefined
//fn.test3(); --> fn.test3 is not a function
//解释 test3 与 test3()
//这里的fn.test3相当于定义了一个属性,但它没有使用过,所以是undefined;
//但在后面加(),test3是一个属性,()表示函数调用,test3()是一个函数,所以会报错:fn.test3 is not a function
console.log("------------------------------------");
//所有函数都是Function的实例(Function是它本身的实例)
console.log(Function.prototype===Function.__proto__);//true
//函数的显示原型指向的对象默认是空Object实例对象(但Object不满足)
console.log(Fn.prototype instanceof Object);//true
console.log(Object.prototype instanceof Object);//false
console.log(Function.prototype instanceof Object);//true
//Object的原型对象是原型链的尽头
console.log(Object.prototype.__proto__);//null
三.原型链的属性问题
1.读取对象的属性值时: 现在自身查找,如果找不到,会自动到原型链中查找
2.设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值
3.方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上
function Fn() { }
Fn.prototype.a = 'xxx'
var fn1 = new Fn()
console.log(fn1.a, fn1) //xxx Fn{},此时属性a在原型链上,不在fn1上
var fn2 = new Fn()
fn2.a = 'yyy'//设置属性值,直接添加在本身
console.log(fn1.a, fn2.a, fn2) //xxx yyy Fn{a: "yyy"}
/*
Fn {a: 'yyy'}
[[Prototype]]: Object
a: "xxx"
*/
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.setName = function (name) {
this.name = name
}
var p1 = new Person('Tom', 12)
p1.setName('Bob')
console.log(p1) //Person {name: "Bob", age: 12}
var p2 = new Person('Jack', 12)
p2.setName('Cat')
console.log(p2) //Person {name: "Cat", age: 12}
console.log(p1.__proto__===p2.__proto__) // true
四.instanceof判断
1.instanceof是如何判断的?
表达式: A instanceof B
如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false
A是一个实例对象,B是一个构造函数
解释:
1.构造函数是一个函数对象,是通过Function构造器产生的
所有函数对象的proto都指向Function.prototype,它是一个空函数(Empty function)
console.log(Foo.__proto__===Function.prototype);//true
console.log(Foo.constructor);//ƒ Function() { [native code] }
console.log(Function.prototype);//ƒ () { [native code] }
//Function.prototypes是一个空函数
2.原型对象本身是一个普通对象(实例对象),而普通对象的构造函数都是Object
所有的构造器也都是一个普通 JS 对象
//实例对象的隐式原型===构造函数的显式原型
console.log(Foo.prototype.__proto__===Object.prototype);//true
3.所有的构造器都是函数对象,并且Object是一个构造函数,函数对象都是 Function
构造产生的
//实例对象的隐式原型===构造函数的显式原型
console.log(Object.__proto__ === Function.prototype);
4.所有函数都是Function的实例(Function是它本身的实例)
Function.prototype===Function.__proto__
5.Function.prototype
也是唯一一个typeof XXX.prototype
为 function
的prototype
。其它的构造器的prototype
都是一个对象
console.log(typeof Function.prototype) // function
console.log(typeof Object.prototype) // object
console.log(typeof Number.prototype) // object
console.log(typeof Boolean.prototype) // object
console.log(typeof String.prototype) // object
console.log(typeof Array.prototype) // object
console.log(typeof RegExp.prototype) // object
console.log(typeof Error.prototype) // object
console.log(typeof Date.prototype) // object
console.log(typeof Foo.prototype) // object
举例1:
function Foo() { }
var f1 = new Foo()
console.log(f1 instanceof Foo) // true
console.log(f1 instanceof Object) // true
举例2:
console.log(Object instanceof Function) // true
console.log(Object instanceof Object) // true
console.log(Function instanceof Function) // true
console.log(Function instanceof Object) // true
function Foo() {}
console.log(Object instanceof Foo) // false
举例3:
function A () {}
A.prototype.n = 1
let b = new A()
A.prototype = { n: 2, m: 3}//这里并不是增加,而是重写
let c = new A()
console.log(b.n, b.m, c.n, c.m) // 1 undefined 2 3
举例4:
function F (){}
Object.prototype.a = function(){
console.log('a()')
}
Function.prototype.b = function(){
console.log('b()')
}
//a()定义在Object的原型上,在源头上,无论怎么调用其实都能找到
//总结一句话:只要在Object原型上定义的方法,任何函数和对象都能看到
let f = new F()
f.a() //a()
f.b() //f.b is not a function -->找不到
F.a() //a()
F.b() //b()
console.log(f) //F {}
console.log(Object.prototype)
console.log(Function.prototype)
五.相关方法
1.Object.getPrototypeOf()
Object.getPrototypeOf
方法返回一个对象的原型。这是获取原型对象的标准方法。
// 空对象的原型是Object.prototype
Object.getPrototypeOf({}) === Object.prototype
// true
// 函数的原型是Function.prototype
function f() {}
Object.getPrototypeOf(f) === Function.prototype
// true
// f 为 F 的实例对象,则 f 的原型是 F.prototype
var f = new F();
Object.getPrototypeOf(f) === F.prototype
// true
2.Object.setPrototypeOf()
Object.setPrototypeOf
方法可以为现有对象设置原型,返回一个新对象。
Object.setPrototypeOf
方法接受两个参数,第一个是现有对象,第二个是原型对象。
var a = {x: 1};
var b = Object.setPrototypeOf({}, a);
// 等同于
// var b = {__proto__: a};
b.x // 1
上面代码中,b
对象是Object.setPrototypeOf
方法返回的一个新对象。该对象本身为空、原型为a
对象,所以b
对象可以拿到a
对象的所有属性和方法。b
对象本身并没有x
属性,但是 JavaScript 引擎找到它的原型对象a
,然后读取a
的x
属性。
举例2:
let obj = {
name: "xiaofeixia"
};
let pjy = {
web: "Pan"
};
//让obj继承hd,即设置obj的原型为hd
Object.setPrototypeOf(obj, pjy);
console.log(obj.web);//Pan
console.log(Object.getPrototypeOf(obj) == pjy); //true
3.Object.create()
该方法接受一个对象作为参数,然后以它为原型,返回一个实例对象。该实例完全继承原型对象的属性。
// 原型对象
var A = {
print: function () {
console.log('hello');
}
};
// 实例对象
var B = Object.create(A);
B.print() // hello
B.print === A.print // true
上面代码中,Object.create
方法以A
对象为原型,生成了B
对象。B
继承了A
的所有属性和方法。这段代码等同于下面的代码。
var A = function () {};
A.prototype = {
print: function () {
console.log('hello');
}
};
var B = new A();
B.print === A.prototype.print // true
可以通过Object.create()方法创建一个极简对象(纯数据字典对象),没有原型(原型为null)
var obj=Object.create(null);
//通过Object.getPrototypeOf来检查它的原型
console.log(Object.getPrototypeOf(obj));//null
Object.create
方法还可以接受第二个参数。该参数是一个属性描述对象,它所描述的对象属性,会添加到实例对象,作为该对象自身的属性。
var obj = Object.create({}, {
p1: {
value: 123,
enumerable: true,
configurable: true,
writable: true,
},
p2: {
value: 'abc',
enumerable: true,
configurable: true,
writable: true,
}
});
// 等同于
var obj = Object.create({});
obj.p1 = 123;
obj.p2 = 'abc';
举例2:
let Pan = { name: "xiaofeixia" };
let pjy = Object.create(Pan, {
p1:{
value: "a person",
enumerable: true
}
});
for (const key in pjy) {
console.log(key);
}
//打印结果
//p1
//name
4.Object.prototype.isPrototypeOf()
对象实例的isPrototypeOf
方法,用来判断一个对象是否是另一个对象的原型。
var o1 = {};
var o2 = Object.create(o1);
var o3 = Object.create(o2);
o2.isPrototypeOf(o3) // true
o1.isPrototypeOf(o3) // true
123456
上面代码表明,只要某个对象处在原型链上,isPrototypeOf
都返回true
。
Object.prototype.isPrototypeOf({}) // true
Object.prototype.isPrototypeOf([]) // true
Object.prototype.isPrototypeOf(/xyz/) // true
Object.prototype.isPrototypeOf(Object.create(null)) // false
5.Object.prototype.hasOwnProperty()
hasOwnProperty()方法用来判断某个对象是否含有指定的自身属性
obj.hasOwnProperty("属性名");//实例obj是否包含有圆括号中的属性
这个方法会忽略那些从原型链上继承的属性。而使用 for/in
遍历时同时会遍历原型上的属性。
而使用in
可以检测原型链上是否存在属性。
let a = { url: "xiaofeixia" };
let b = { name: "Pan" };
Object.setPrototypeOf(a, b);
console.log("name" in a);//true
console.log(a.hasOwnProperty("name"));//false
console.log(a.hasOwnProperty("url"));//true
用in
遍历:
使用 for/in
遍历时同时会遍历原型上的属性
let Pan = { name: "xiaofeixia" };
let pjy = Object.create(Pan, {
p1:{
value: "a person",
enumerable: true
}
});
for (const key in pjy) {
console.log(key);
}
//打印结果
//p1
//name