原型及其相关

1 构造函数

ECMAScript中的构造函数是用于创建特定类型对象的。Object和Array是原生构造函数,运行时可以直接在执行环境中使用,也可以自定义构造函数。
构造函数与普通函数唯一的区别就是调用方式不同。除此之外,构造函数也是函数。任何函数只要使用new操作符调用就是构造函数,而不使用new操作符调用的函数就是普通函数。
按照惯例,构造函数名称的首字母要大写,非构造函数则以小写开头。

// 当作构造函数使用
var person = new Person("Nicholas", 29, "Software Engineer"); 
person.sayName(); //"Nicholas" 
// 作为普通函数调用
Person("Greg", 27, "Doctor"); // 添加到 window 
window.sayName(); //"Greg" 
// 在另一个对象的作用域中调用
var o = new Object(); 
Person.call(o, "Kristen", 25, "Nurse"); 
o.sayName(); //"Kristen"

2 理解原型

无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 prototype 属性(指向原型对象)。在默认情况下,所有原型对象都会自动获得一个 constructor (构造函数)属性,指回与之关联的构造函数。就拿前面的例子来说, Person.prototype. constructor 指向 Person。然后,因构造函数而异,可能会给原型对象添加其他属性和方法。
(以上这段话光看有些难以理解,但是结合以下画图就好理解了)
在这里插入图片描述

在自定义的构造函数时,原型对象默认只会取得 constructor 属性;其他的所有方法都继承自Object 。每次调用构造函数创建一个新实例,这个实例的内部[[Prototype]]指针就会被赋值指向构造函数的原型对象。脚本中没有标准的方式访问[[Prototype]],但 Firefox、Safari 和 Chrome 在每个对象上暴露__proto__属性;通过这个属性可以访问对象的原型。在其他实现中,这个特性完全被隐藏了。关键在于理解这一点:实例与构造函数的原型对象之间有直接的联系,但实例与构造函数之间没有。


重写整个原型会切断最初原型与构造函数的联系,但实例引用的仍然是最初的原型。、
实例只有指向原型的指针,没有指向构造函数的指针。
重写原型的正确方式:

function Person(){} 
Person.prototype = { 
 // constructor: Person, 
 name : "Nicholas", 
 age : 29, 
 job : "Software Engineer", 
 sayName : function () { 
 alert(this.name); 
 } 
};
//恢复constructor属性
Object.defineProperty(Person.prototype, "constructor", {
  enumerable: false,
  value: Person
})

3 运算符

3.1 instance

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
(检查实例的原型链中是否包含指定构造函数的原型)
在这里插入图片描述

对以上代码的通俗理解:
创建Car构造函数,auto为Car原型对象的一个实例,auto实例的原型链中是否包含Car的原型,因为auto为Car原型对象的一个实例,即auto有一个指向Car原型对象的一个指针,所以auto实例的原型链中有包含Car的原型。

3.2 object

3.2.1 Object()

创建一个新的 Object 对象。该对象将会包裹(wrapper)传入的参数
Object 构造函数将给定的值包装为一个新对象。

  • 如果给定的值是 nullundefined, 它会创建并返回一个空对象。
  • 否则,它将返回一个和给定的值相对应的类型的对象。
  • 如果给定值是一个已经存在的对象,则会返回这个已经存在的值(相同地址)。

在非构造函数上下文中调用时, Object 和 new Object()表现一致。

3.2.2 Object.create()

使用指定的原型对象和属性创建一个新对象。
接收两个参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)
在只有一个参数时,Object.create()与某个object()方法效果相同。红宝书P245
替代Object.setPrototypeOf()->可能造成性能降低

3.2.3 判断属性存在于实例还是原型上

hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
(只有属性存在于实例上时才返回true)
hasPrototypeProperty() 属性存在于原型上时才返回true

4 盗用构造函数

基本思路:在子类构造函数中调用父类构造函数。因为毕竟函数就是在特定上下文中执行代码的简单对象,所以可以使用apply()和call()方法以新创建的对象为上下文执行构造函数。(使用call实现继承)

function SuperType() { 
 this.colors = ["red", "blue", "green"]; 
} 
function SubType() { 
 // 继承 SuperType 
 SuperType.call(this);    
 //call中的this指针指向SuperType,先改为指向this,并执行SuperType()
} 
let instance1 = new SubType(); 
instance1.colors.push("black"); 
console.log(instance1.colors); // "red,blue,green,black" 
let instance2 = new SubType(); 
console.log(instance2.colors); // "red,blue,green"

https://www.jianshu.com/p/43a996db58e7
call方法的作用:
函数名.call(对象,arg1…argn)
1.将this指向第一个参数
2.将第二个及以后所有的参数,作为实参传递给函数
(让原型上的 call 方法执行,在执行 call 方法时,让调用 call 的方法中的 this 指向 call 方法里的第一个参数,再执行调用 call 的这个方法。)

注意事项
1.call和apply只是传参方式的区别,apply可以通过数组的方式传参
2.call和apply的第一个参数如果是null和underfined,那么this会指向windows
3.call和apply的第一个参数如果为值类型的数据,那么会将这个值类型的数据,转换成其对应的引用类型的数据,然后再将this指向这个引用类型的数据
4.call和apply立即执行这个函数,bind方法可以让对应的函数想什么时候调就什么时候调用,并且可以将参数在执行的时候添加,这是它们的区别,根据自己的实际情况来选择使用。
5.当参数的个数确定的情况下可以使用call;当参数的个数不确定的情况下可以使用apply

5 组合继承

基本的思路:使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。这样既可以把方法定义在原型上以实现重用,又可以让每个实例都有自己的属性。

function SuperType(name){ 
 this.name = name; 
 this.colors = ["red", "blue", "green"]; 
} 
SuperType.prototype.sayName = function() { 
 console.log(this.name); 
}; 
function SubType(name, age){ 
 // 继承属性
 SuperType.call(this, name); 
 this.age = age; 
} 
// 继承方法
SubType.prototype = new SuperType(); 
SubType.prototype.sayAge = function() { 
 console.log(this.age); 
}; 
let instance1 = new SubType("Nicholas", 29); 
instance1.colors.push("black"); 
console.log(instance1.colors); // "red,blue,green,black" 
instance1.sayName(); // "Nicholas"; 
instance1.sayAge(); // 29 
let instance2 = new SubType("Greg", 27); 
console.log(instance2.colors); // "red,blue,green" 
instance2.sayName(); // "Greg"; 
instance2.sayAge(); // 27

保留instance操作符和isPrototypeOf()方法识别合成对象的能力。

6 总结

1.原型链实现继承,缺点:但是包含引用值时,原型实际上变成了另一个类型的实例,即原先的实例属性变成了原型属性。
2.盗用构造函数解决了原型包含引用值导致的继承问题,
缺点:必须在构造函数中定义方法,因此函数不能重用。此外,子类也不能访问父类原型上定义的方法,因此所有类型只能使用构造函数模式。
(也是使用构造函数模式自定义类型的问题)
3.组合继承弥补了原型链和盗用构造函数的不足。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值