写在最前:构造函数和原型模式的使用场景很广泛,但因为对概念的混淆不清导致无法熟练掌握。切图带你从代码和流程图一步步攻克,纯干货,建议收藏详看,原型模式理解图非常重要,务必多看几遍!
前往查看demo源码
js基础(一):判断类型
// 构造函数
function Person(name, age) {
this.name = name
this.age = age
this.introduction = function() {
console.log(`my name is ${this.name}, I'm ${this.age} years old`)
}
//return this //构造函数默认有这句
}
var p = new Person('qietuniu', 18) // this=Person
p.introduction()
// 普通函数
function person(name, age) {
this.name = name
this.age = age
this.introduction = function() {
console.log(`my name is ${this.name}, I'm ${this.age} years old`)
}
return `直接返回:我的名字 ${this.name}, 我 ${this.age} 岁`
}
console.log(person('qietuniu', 18)) //this=window
window.introduction()
构造函数内的上下文this指向即将要生成的实例对象Person,普通函数内使用this,指向window时容易造成全局污染
。该构造函数将陪着我们读完这篇文章,之后的示例将在这基础上演示!
语法糖:更简单表达一个操作的语法,能够增加程序的可读性,在性能上不会带来损失的同时提高开发编码的效率,从而减少程序代码出错的机会!
可使用instanceof判断一个函数是否是一个变量的构造函数.
解析:instanceof的判断逻辑是实例p的__proto__一层一层往上,能否对应到Person.prototype,同样也能到Object.prototype.
查看instanceof具体使用>>
解析(以Person函数为例):
1.创建一个新对象p
2.将构造函数Person()中的this指向新创建的对象p
3.p的_proto_(隐式原型)属性指向Person函数的prototype(显示原型),创建构造函数与原型以及对象的关系
4.调用对象,执行Person内属性或方法
Person.prototype.constructor = Person
p的__proto__.constructor = Person
p的__proto__ =Person.prototype
Person.prototype的__proto__ = Object.prototype
Object.prototype的__proto__ = null
Person.prototype的__proto__的__proto__ = null
熟记该图,万变不离其宗
1. 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(null除外)
var obj = {}
obj.a = 100 //自由扩展属性
var arr = []
arr.a = 100
function fn() {}
fn.a = 100
创建对象的三种方法
// 字面量
var o1 = {
name: 'o1'
}
var o2 = new Object({
name: 'o2'
})
// 构造函数
var M = function() {
this.name = 'o3'
}
var o3 = new M()
// Object.create
var O = {
name: 'o4'
}
var o4 = Object.create(O)
console.log(o1)
console.log(o2)
console.log(o3)
console.log(o4)
2. 所有的引用类型(数组、对象、函数),都有一个__proto__属性(隐式原型),属性是一个普通的对象
//隐式原型
console.log(obj.__proto__)
console.log(arr.__proto__)
console.log(fn.__proto__)
3. 所有的函数,都有一个prototype属性(显式原型),属性也是一个普通的对象
//显式原型
console.log(fn.prototype)
4. 所有的引用类型(数组、对象、函数),_proto_属性值指向它的构造函数的“prototype”的值
//_proto_属性值指向它的构造函数的“prototype”的值
console.log(`arr.__proto__ === Array.prototype:${arr.__proto__ === Array.prototype}`)
console.log(`obj.__proto__ === Object.prototype:${obj.__proto__ === Object.prototype}`)
console.log(`fn.__proto__ === Function.prototype:${fn.__proto__ === Function.prototype}`)
5. 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__(即它的构造函数的prototype)中寻找
Person.prototype.sayName = function() {
console.log(`我的名字:${this.name}`)
}
p.introduction()
p.sayName()
执行sayName时的时候,对象p本身没有该方法,会去它的__proto__即它的构造函数的prototype中寻找(p.__proto__或者Person.prototype),于是找到sayName.
什么是原型对象
Person这个构造函数的显式原型是一个对象,简称原型对象。Person.prototype
就是原型对象。
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针,即原型对象(Person.prototype)是 构造函数(Person)的一个实例。
Person.prototype = p.proto
原型对象的优点:
可以 让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是 可以将这些信息直接添加到原型对象中,比如下面的sayName方法。
Person.prototype.sayName = function() {
console.log(`我的名字:${this.name}`)
}
如何查找对象自身的属性
var item
for (item in p) {
// 高级浏览器已经在for in中屏蔽了来自原型的属性
// 以下的判断可保证程序的健壮性,hasOwnProperty方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性
if (p.hasOwnProperty(item)) {
// 输出name和printName,没有alerName
console.log(item)
}
}
ECMAScript 中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
p.toString()是如何调用的
执行toString方法时,p本身没有该方法,p.__proto__也没有,继续往上p.proto.__proto__即Person.prototype.proto,Person.prototype就是普通对象,Person.prototype.proto = Object.prototype,Object中存在toString方法。
原型链图
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型。对象的内部指针这么一层一层的查找就是原型链查找,如此层层递进,就构成了实 例与原型的链条,这种链式结构叫做“原型链“。
jquery中原型的使用
jQuery.fn.init.prototype = jQuery.fn,将原型方法为什么放在jQuery.fn中,是因为要扩展插件如下面的printQT 方法, 只有$会暴露在window全局变量(太多会造成污染),将插件扩展统一到jQuery.fn.xxx这一个接口方便使用。
var jQuery = function() {
return new jQuery.fn.init();
}
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function() {
this.jquery = "1.9.1";
return this;
}
}
jQuery.fn.init.prototype = jQuery.fn;
jQuery.fn.printQT = function() {
console.log("切图")
return this;
}
window.jQuery = window.$ = jQuery;
console.log(jQuery().printQT())
其他
除了jquery中的运用,在vue中使用诸如echarts的插件时,我们会使用Vue.prototype.$echarts = echarts,将echarts引入到全局使用,同样自定义方法变量也可以如此使用。
尊重原创,如需转载请注明出处!