在网上参看了一些博客之后还是觉得模糊。于是自己动手实验了一下,记录
几乎所有对象都有原型,但是只有少数对象有prototype(函数对象).有__proto__原型的一定是对象,有prototype原型的一定是函数,函数既有__proto__又有prototype
Object.prototype没有原型
Object.create(null)新创建的对象也没有原型,没有继承任何方法
箭头函数也没有
有prototype属性的对象为其他所有对象定义了原型,构造器具有prototype和__proto__,单纯实例只具有__proto__。任何普通的JavaScript函数(不包括箭头函数、生成器函数、异步函数)都可以做构造函数,自动拥有一个prototype属性对象,该对象有一个不可枚举的constructor属性(值为该函数对象)
__proto__表明实例的原型指向,作为对象的内部属性prototype是不能直接访问的,所以,为了查看对象原型,Firefox和Chrome内核的JavaScript引擎中提供了这个__proto__非标准访问器
ECMA新标准引入了标准对象原型访问器Object.getPrototype(object)
匿名函数和箭头函数
上文提到,函数对象有prototype,但是注意箭头函数的this已经绑定了定义时候的上下文,不会根据调用它的对象来动态设置。它也没有prototype属性,因此不能作为构造函数使用
基础知识
prototype属性是一个很特别的属性,如果new一个新的对象,浏览器会自动把prototype中的内容附加在对象上。通过利用prototype就可以在JavaScript中实现成员函数的定义,甚至是“继承”的效果。
每个构造函数都会有一个prototype属性,该属性是一个原型对象,包含应该由指定类型的实例共享的属性和方法。原型对象有一个不可枚举的constructor属性指向与之关联的构造函数,即该函数对象。
对象有属性:隐式对象 __ proto __,指向该对象的构造函数的原型对象。
在创建函数时,也会创建其原型对象,同时自动给这个原型的constructor属性赋值。所以如果用对象字面量重写原型对象,意味着constructor属性会被改写指向Object。这时就不能通过constructor来识别类型。
图注解:
- 对象有属性 __ proto __,指向该对象的构造函数的原型对象。方法(函数)除了有属性 __ proto __,还有属性prototype,prototype指向该方法的原型对象。
- 实例与构造函数原型之间有直接链接,可以用 className.prototype.isPrototypeOf(实例名);检测。但是实例与构造函数之间没有,他是间接通过原型的constructor属性访问构造函数。instance.constuctor取决于instance.__ proto __.constructor
- 只有.prototype对象才实际拥有constructor属性,其他对象都是通过原型链间接获得
console.log(obj.constructor===obj.__proto__.constructor);//always true
instanceof操作符
其源码原理大概如下:
function new_instance_of(leftVaule, rightVaule) {
let rightProto = rightVaule.prototype; // 取右表达式的 prototype 值
leftVaule = leftVaule.__proto__; // 取左表达式的__proto__值
while (true) {//只要右边变量的 prototype 在左边变量的原型链上即可
if (leftVaule === null) {
return false;
}
if (leftVaule === rightProto) {
return true;
}
leftVaule = leftVaule.__proto__
}
}
构造函数
定义一个函数,用new实例化
function A(){};
let a=new A();
console.log(A.prototype.constructor);//ƒ A(){}
console.log(a.constructor);//ƒ A(){}
console.log(a instanceof A);//true
修改实例的proto属性,相当于切断与构造器的连接,重新定义a.constructor
a.__proto__ = {};//修改实例的隐式原型对象,相当于切断与构造器的连接
console.log( A.prototype.constructor);//ƒ A(){}
console.log(a.constructor);//ƒ Object() { [native code] }
console.log(a instanceof A);//false
又或者修改构造器的原型,注意这里是构造器方法,constructor会改变
A.prototype={
id:7
};//只影响后面的,不影响前面已经生成的
console.log(A.prototype.constructor);//ƒ Object() { [native code] }
console.log(a.constructor);//ƒ A(){} 前面已经生成的不受影响
console.log(a instanceof A);//false
let aa=new A();
console.log(aa.constructor);//ƒ Object() { [native code] }之后实例化的生效
console.log(aa instanceof A);//true
字面量重定义原型后,实例化的对象才会继承新的原型。
这里疑惑
instanceof不是只需要原型链上存在就好了吗?
a的原型链上应该有f Object(),我打印出来看看,注意这里A.prototype没有出现在a的原型链上
let count=0
while (true) {//只要右边变量的 prototype 在左边变量的原型链上即可
if (a === null) {
console.log("null");//ƒ A(){}
break;
}
count++;
console.log(a.constructor);//ƒ A(){}
a = a.__proto__
}
console.log(count);
count为3
两个ƒ A(){}和一个ƒ Object() { [native code] }
存疑实在是迷惑,希望有大佬帮忙解释一下到底是instanceof的原理另有玄机还是?
构造器类
类和方法的constructor属性对于自己的意义不一样。
构造函数可以不用new调用(以全局的this作为内部对象通常是window),类构造器必须要用new调用(类内部通过constructor函数实例化)
让我们先定义一个类,再new一个实例看看
类和函数在这里有所不同,主要表现在类与constructor的关系更紧密,是自己获得的,详见修改构造器类的情况。
class ClassA{};
let a=new ClassA();
console.log(ClassA.prototype.constructor);//class ClassA{}
console.log(a.constructor);class ClassA{}
console.log(a instanceof ClassA);//true
修改实例的proto属性,相当于切断与构造器的连接,重新定义a.constructor
a.__proto__ = {};//修改实例的隐式原型对象,相当于切断与构造器的连接
console.log( ClassA.prototype.constructor);//class ClassA{}
console.log(a.constructor);//Object() { [native code] }
console.log(a instanceof ClassA);//false
又或者修改构造器类的原型,注意对比构造器方法,因为这里是类,所以constructor默认都是类
ClassA.prototype={
id:7
};//修改构造函数的原型对象不影响
console.log(ClassA.prototype.constructor);//class ClassA{}
console.log(a.constructor);class ClassA{}
console.log(a instanceof ClassA);//true
let aa=new ClassA();
console.log(aa.constructor);class ClassA{}
console.log(aa instanceof ClassA);//true
修改构造器
ClassA={};
console.log(a.constructor);//class ClassA{}
console.log(a instanceof ClassA);//Right-hand side of 'instanceof' is not callable
console.log(ClassA.prototype);//undefined
console.log(ClassA.prototype.constructor);//Cannot read property 'constructor' of undefined
总结
学习语法时候注意区分类和方法
function A(){}
let a=new A();
A.prototype={
id:7
};//修改构造函数的原型对象不影响
console.log(a.id);//undefined
let aa=new A();
console.log(aa.id);//7
console.log(A.prototype.id);//7
怎么解释?存疑?类相比函数是更纯白的存在,
她是需要构造函数等去充盈自身的,所以{}即没有prototype属性?
类定义没有显式支持在原型上或者类上添加成员数据,但在类定义外部可以手动添加
class A{};
let a=new A();
A.prototype={
id:7
};//修改构造函数的原型对象不影响
console.log(a.id);//undefined
let aa=new A();
console.log(aa.id);//undefined
console.log(A.prototype.id);//undefined
一些浏览器支持对象有__proto __属性,但是__proto __属性貌似不会被继承。
class A{};
let a=new A();
A.__proto__={
id:7
};//修改构造函数的原型对象不影响
console.log(a.id);//undefined
let aa=new A();
console.log(aa.id);//undefined
console.log(A.__proto__.id);//7