有关prototype和__proto__的学习记录

在网上参看了一些博客之后还是觉得模糊。于是自己动手实验了一下,记录

几乎所有对象都有原型,但是只有少数对象有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来识别类型。

在这里插入图片描述

图注解:

  1. 对象有属性 __ proto __,指向该对象的构造函数的原型对象。方法(函数)除了有属性 __ proto __,还有属性prototype,prototype指向该方法的原型对象。
  2. 实例与构造函数原型之间有直接链接,可以用 className.prototype.isPrototypeOf(实例名);检测。但是实例与构造函数之间没有,他是间接通过原型的constructor属性访问构造函数。instance.constuctor取决于instance.__ proto __.constructor
  3. 只有.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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值