笔记: js构造函数与原型
目录
@
构造函数与原型介绍
1.函数与函数的原型对象(prototype object):
- 在javascript中,创建一个函数a, 浏览器就会在内存中创建一个对象b,而且该函数默认会有一属性 prototype 指向这个对象(即:prototype属性的值)
- 这个对象b就是函数a的原型对象,简称函数的原型。原型对象b也默认会有一个属性 constructor 指向了这个函数a (即:constructor属性的值是函数a)
- 凡是以函数a为构造函数而创建的对象a1,a2,a3等等,也都有一个内部的[[prototype]]属性,也指向这个对象b.
构造函数,原型对象,实例对象之间的三种重要引用:
(1) 构造函数-->原型对象 (a.prototype-->b)
(2) 原型对象-->构造函数 (b.constructor-->a)
(3) 实例对象-->原型对象 (a1.[[prototype]]/a1._ proto _-->b)
涉及三种引用的操作
2.基于三种引用的操作
如上图,我们基于这三种引用会有许多的操作(修改,替换,删除),我们来进行一个分析总结.
-
构造函数创建实例的具体过程
参考资料:
英文版-- [[construct]] [[call]]
中文版-- [[construct]] [[call]]
new a();
1.令ref = 构造函数a的引用(地址)
2.令变量constructor = getvalue(ref): 按照ref找到函数对象a的存储单元
3.调用constructor的[[construct]]内部方法:- 创建一个新的原生javascript对象obj
- 按照规范设定好obj对象的一系列内部属性和方法
- 设置obj的[[prototype]] (设置obj的原型)
- 如果a.prototype是一个对象,令obj.[[prototype]]=a.prototype
- 如果a.prototype不是一个对象,令obj.[[prototype]]=object.prototype
- 如果a.prototype是一个对象,令obj.[[prototype]]=a.prototype
- 令变量result = a.[[call]] (其中,this值为obj)
(就是以obj为this的值,执行a的函数体中的代码,目的是对obj进行初始化)
总结: 由上面的分析可知,如果在构造函数a的函数体内用this给实例添加的属性,是不会反映到原型上的,属于实例的本身的属性.
- 创建一个新的原生javascript对象obj
-
三种引用是否可以被更改的测试
//test:三种引用是否都可以修改替换
function a (){}
var b = a.prototype;
var a1 = new a();//a.prototype与b.constructor
console.log(object.getownpropertydescriptor(a,'prototype'));//可修改
console.log(object.getownpropertydescriptor(b,'constructor'));//可修改
//[[prototype]]
console.log('prototype' in a1); //false,内部属性不属于原型属性
console.log(a1.hasownproperty('prototype'));//false,内部属性不属于自身属性
//只有获取方法,没有手动修改方法
//__ proto __
console.log(' __ proto __ ' in a1);
console.log(a1.hasownproperty(' __ proto __ '));//false, __ proto __ 属于原型属性
console.log(object.prototype.hasownproperty(' __ proto __ '));//true,__ proto __ 定义在object.prototype上
console.log(object.getownpropertydescriptor(object.prototype, ' __ proto __ '));//configurable:true enumerable:false
//利用 __ proto __ 间接修改[[prototype]] (不推荐)
function c() {}
var d = c.prototype;
console.log(object.getprototypeof(a1));
a1. __ proto __ = d; //利用非规范属性 __ proto __ 间接修改[[prototype]]
console.log(object.getprototypeof(a1));总结: __ proto __属性是非标准的,是定义在object.prototype上的一个暴露实例内部[[prototype]]属性的访问器属性.如果我们考虑到代码的安全和性能,我们可以在代码开始位置用
delete objet.prototype. _ _ proto _ _
来删除掉. 替换构造函数a的原型--修改a.prototype的值
预计的影响:
1.已有实例的原型不变,但无法再用a.prototype添加或修改原型属性
2.新实例的原型是a.prototype修改后的值,并且可以用a.prototype添加或修改原型属性
//test:构造函数替换原型对象的影响
function a() {}
function b() {}
var a1 = new a();
var b1 = new b();
a.prototype.say = function (){
alert('a链的方法');
}
b.prototype.say = function (){
alert('b链的方法');
}
var temp = b.prototype;
b.prototype = a.prototype;
a.prototype = temp;
var a2 = new a();
var b2 = new b();
//检测a1 a2 b1 b2 各自的原型链
a1.say();
b1.say();
a2.say();
b2.say();
//尝试通过原有构造函数向a1添加原型属性
a.prototype.say2 = function (){
alert('仍可以通过a向a1添加原型属性');
}
a.prototype.say3 = function (){
alert('可以通过a向a2添加原型属性');
}
alert('say2' in a1);//false,a1.say2方法不存在.不能再通过a向a1添加原型属性
a2.say3();//添加成功替换已有实例的原型
预计影响:
1.接上了另一条原型链
2.无法再用a.prototype添加或修改原型属性
//test:已有实例对象修改原型对象的影响
function a() {}
function b() {}
var a1 = new a();
var b1 = new b();
a.prototype.say = function (){
alert('a链的方法');
}
b.prototype.say = function (){
alert('b链的方法');
}
//测试是否接到另一条原型链
var a2 = object.create(a1);
a2.say();
a2.__ proto __ = b1;
a2.say();
//测试是否不能再用原来的构造函数添加原型属性
var a3 = new a();
a3.__ proto __ = b1;
a.prototype.say2 = function (){
alert('仍然可用构造函数添加原型属性');
}
a3.say2(); //出错,a3中找不到方法say2()替换原型的构造函数--a.prototype.constructor
影响:
1.只是无法再用a.prototype.constructor获取构造函数a,无法便捷地添加'静态方法'了
2.仍能正常用a创建实例,用a.prototype添加或修改原型属性
有关原型及原型链的一些相关方法总结
1.instanceof运算符
参考资料: [[hasinstance]](v)
instaceof运算符,是将左边对象的原型作为参数,来调用右边函数的[[hasinstance]] (v)内部方法,所得返回值即为运算符的结果.
[[hasinstance]] (v)方法大致过程:(以a.[[hasinstance]] (v)为例)
- 1.令v=v.prototype
- 2.如果v不是null,比较a.prototype与v的值
- 如果相等,返回true
- 如果不等,从1重新开始,直到v为null
//test: instanceof原理测试:向上搜索实例的原型链,看由构造函数所指向的原型对象是否在其中
function a() {}
var a1 = new a();
var b = a.prototype;
console.log(a1 instanceof a);//true
b.constructor = null;
console.log(a1 instanceof a);//true
a.prototype = {};
console.log(a1 instanceof a);//false
2.属性遍历
- 1.自身可枚举一般属性遍历:(enumerable为true) "object.keys()+循环语句"或"hasownproperty()/getownpropertynames()+propertyisenumerable()+循环语句"
- 2.所有可枚举一般属性遍历:for...in循环
- 3.自身所有一般属性: hasownproperty()/getownpropertynames()+循环语句
- 4.原型链所有一般属性: 用一个循环遍历整个原型链,对里面的每一个原型都应用3的方案
注:欢迎转载,转载请注明出处
上一篇: CAD怎么选中被覆盖的图元?
下一篇: 踩坑之默认输入法配置