js 原型与原型链
原型
原型的概念的理解
- 原型是function构造函数的一个属性,它定义了构造函数制造出的对象的公共祖先,通过此构造函数产生的对象,继承了该原型的属性和方法,原型也是对象
- 所有的引用类型都有一个’_ _ proto_ _'属性(也叫隐式原型,它是一个普通的对象)。
- 所有的函数都有一个’prototype’属性(这也叫显式原型,它也是一个普通的对象)。
- 所有引用类型,它的’_ _ proto_ _'属性指向它的构造函数的’prototype’属性。
- 当试图得到一个对象的属性时,如果这个对象本身不存在这个属性,那么就会去它的’_ _ proto_ _'属性(也就是它的构造函数的’prototype’属性)中去寻找。
构造函数及prototype
function Person() {
this.name = "aaa";
};
Person.prototype //此时,会输出
{
constructor:f Person()
_proto_:Object
}
说明:
//Person.prototype 就是Person函数的原型,它也是一个对象,此对象中的constructor指针,指向Person构造函数本身,
//其目的就是不管自己的子孙(即Person构造函数构造的所有对象)走到哪儿,都知道自己是谁生的,友友们理解。。。;
//_proto_指针指向了Object函数的原型(后面再详细分析);
修改构造函数原型,其对象是否继承
- 原型上添加属性或方法
Person.prototype.name = "a";
function Person() {}
--此时,Person.prototype对象中会多出一个属性name,值为a;
var per1 = new Person(); //用构造函数创建对象
// 此时per1对象没有prototype属性,但per1对象有_proto_属性,这是怎么回事了啦,其实是这样的,
// 我们在用new 关键字创建对象时,其时js会偷偷的在构造函数创建出的对象里发生下面的事
this._proto_ = {_proto_:Person.prototype,}
所以会有,per1._proto_指针指向Person函数原型的结果
因此,per1.name // 会输出 a per1通过_proto_属性找到了Person函数原型下的name属性;
- 覆盖默认原型
Person.prototype.name = "a";
function Person() {}
// 此时 Person函数的原型会是
{
name:"a"
constructor:ƒ Person()
__proto__:Object
}
//若作如下修改
Person.prototype= {
name:"ccc"
};
//此时,Person函数的原型会是
{
name: "ccc"
__proto__: Object
}
//覆盖constructor和原来添加的原型属性
原型链
原型的构成及原型链上属性的修改
A.prototype.lastname = "我是a";
function A() {
this.a = "A";
}
var a = new A();
// 把a对象复制给B函数的原型
B.prototype = a;
function B() {
this.b = "B";
this.num = 100;
this.fortune = {
"car":"BM"
}
}
var b = new B();
// 把b对象赋值给C函数的原型
C.prototype = b;
function C() {
this.c = "C";
}
var c = new C();
1、构成原型链
以上代码构成如下的原型链条
说明:
首先,c的构造函数是C()。所以:
c. _ proto _=== C.prototype === b,以此类推,b同c,
a的构造函数是A()。所以:
a. _ proto _=== A.prototype ,
又因为A.prototype是一个普通的对象,它的构造函数是Object,所以:
a.prototype. _ proto _=== Object.prototype
**2、原型链上属性的操作
**
2.1
执行:
c.num++;
操作
c.num // 输出101
b.num; //101
分析:c.num++相当于
c.num = c.num+1;等式右边的c.num通过原型链c._proto_找到C的原型b下面的num=100;拿到值加1为101,
然后在赋值给 c.num,相当于给c对象添 加值为101的属性num,截图如下:
2.2
执行:c.fortune = {car:‘byd’};
c.fortune.car; // 输出 byd
b.fortune.car //输出 BM
原理同上,不再赘述。
3、原型链上的操作2
Dog.prototype = {
height:100,
}
function Dog() {
this.hanlder = function () {
this.height++;
}
}
var dog1 =new Dog();
//说明:
//dog1.height // 增加了height属性,并赋值为101
//原型不变
创建对象的方式,
字面量方式
var a= {};
构造器方式
function A(){}
var a = new A();
object.create方式
var bbb = {}
var a = Object.create(null); // a 对象无原型链
var a = Object.create(bbb);
call/apply
作用及区别
作用:改变this指向,通俗点就是借用别人的函数实现自己的逻辑
区别:传参形式不同
调用
function P(name,age) {
this.name = name;
this.age = age
}
var p = new P("XX",20);
var obj = {};
//正常方法test()相当于test.call();
//正常调用函数时,不new 内部this指向window new 后函数内部生成this = {}
P.call(obj,"bbb",55);// this指向obj call需要把实参按照形参的个数传递
p.apply(obj,["bbb",55]); // this指向obj apply需要传递一个arguments
//此时
obj = {
name: "bbb",
age: 55
__proto__:Object
}
利用call/apply实现代码的优化
优化前:
function teacher(name,age) {
this.name = name;
this.age = age;
}
function student(name,age,sex) {
this.name = name;
this.age = age;
this.sex =sex;
}
var teacher= new teacher("aa",23)
var student = new student("aa",23,"man")
优化后
function teacher(name,age) {
this.name = name;
this.age = age;
}
//利用优化后
function student(name,age,sex) {
teacher.call(this,name,age); //call需要把实参按照形参的个数传递
//teacher.apply(this,[name,age]) // apply需要传递一个arguments
this.sex =sex;
}
var student = new student("aa",23,"man")