js原型与继承
1 原型的初步认识
1.1 代码体验
- 原型可以理解为父一级 proto
/**
* 普通对象
*/
let obj = {};
console.log(obj) //__proto__: Object
1.4 原型方法与对象方法优先级
- 对象中自己有的用自己的,没有再找原型
/**
* 原型方法与对象方法相比较
*/
let hi = {
show() {
console.log('对象方法')
}
}
console.log(hi)
hi.__proto__.show = function() {
console.log('原型方法')
}
hi.show() //对象方法
1.5 函数有多个长辈
-
函数可以当对象使用
- 通过__proto__访问对象中的属性和方法
-
也可以当构造函数使用
- 通过prototype能够定义实例对象的属性与方法
1.7 系统构造函数的原型体现
let arr = []
console.log(arr.__proto__ === Array.prototype)
let obj = {}
console.log(obj.__proto__ === Object.prototype)
let str = ''
console.log(str.__proto__ === String.prototype)
let bool = true
console.log((bool.__proto__ === Boolean.prototype))
let reg = /a/g
console.log(reg.__proto__ === RegExp.prototype)
1.8 自定义对象的原型设置
let obj = {
name: 'hi'
}
let parent = {
name: 'parent'
}
// 获取原型
console.log(Object.getPrototypeOf(obj) === Object.prototype) //true
// 设置原型
Object.setPrototypeOf(obj,parent)
console.log(Object.getPrototypeOf(obj) === parent) //true
1.9 原型中constructor引用
- __proto__与prototype区别
- _proto_ 服务自己
- prototype 通过构造函数new出来无数个对象时使用这个,这个功能比较强大
function User(name) {
this.name = name
}
// prototype原型中的constructor指向构造函数
console.log(User.prototype.constructor === User) // true
let zs = new User('张三')
let ls = new User.prototype.constructor('李四')
console.dir(zs.name)
console.dir(ls.name)
**************************************************************
function User(name) {
this.name = name
age: 22
}
// 定义多个方法时,会覆盖,要加上constructor,这个对象要放在实例化对象之前,否则会报错
User.prototype = {
constructor: User,
showName() {
console.log(this.name)
},
showAge() {
console.log(this.age)
}
}
// prototype原型中的constructor指向构造函数
// console.log(User.prototype.constructor === User) // true
let zs = new User('张三')
let ls = new User.prototype.constructor('李四')
// console.dir(zs.name)
// console.dir(ls.name)
// 定义单个方法
User.prototype.sayHi = function () {
console.log('hi ' + this.name)
}
console.dir(User)
zs.sayHi()
ls.sayHi()
zs.show()
1.10 给我一个对象,还你一个世界(根据对象创建对象)
// 根据对象创建对象
function User(name) {
this.name = name
}
let zs = new User("张三")
console.log(zs)
function createByObject(obj,...args){
// 通过已知的对象找到该对象的原型,通过原型找到构造函数
const constructor = Object.getPrototypeOf(obj).constructor
return new constructor(...args)
}
let ls = createByObject(zs,'李四')
console.log(ls)
1.11 原型链总结
- prototype 原型是一个对象,相应的:任何一个对象都可以成为其他对象的原型
- 在定义方法时,将共用的放在父级原型中,实现子级的继承
// 原型链检测 instanceof
function A() { }
function B() { }
function C() {}
let c = new C()
B.prototype = c
let b = new B()
A.prototype = b
let a = new A()
// 构造函数A 是否在 对象a 的原型链上
console.log(a instanceof A)
// 构造函数B 是否在 对象b 的原型链上
console.log(a instanceof B)
// 构造函数A 是否在 对象c 的原型链上
console.log(a instanceof C)
1.12 原型链检测
- object instanceof constructor
- prototypeObj.isPrototypeOf(object)
// 原型链检测 instanceof
function A() { }
function B() { }
function C() {}
let c = new C()
B.prototype = c
let b = new B()
A.prototype = b
let a = new A()
// 构造函数A 是否在 对象a 的原型链上
console.log(a instanceof A)
// 构造函数B 是否在 对象b 的原型链上
console.log(a instanceof B)
// 构造函数A 是否在 对象c 的原型链上
console.log(a instanceof C)
// 原型链检测 isPrototypeOf 检测
// 构造函数C的原型对象 是否在 对象 a 的原型链
console.log(C.prototype.isPrototypeOf(a))
// 原型对象c 是否在 对象 a 的原型链
console.log(c.isPrototypeOf(a))
console.log(B.prototype.isPrototypeOf(a))
console.log(C.prototype.isPrototypeOf(a))
console.log(A.prototype.isPrototypeOf(c))
1.14 in与hasOwnProperty属性检测差异
- in 在检测属性时,不仅检测自身的属性,还检测原型链上的属性
- hasOwnProperty只检测自身的属性
- obj.hasOwnProperty(prop)
let a = {
name: 'tom'
}
let b = {
age: 22
}
Object.setPrototypeOf(a,b)
console.log('name' in a) //true
console.log('age' in a) //true
console.log(a.hasOwnProperty('name')) //true
console.log(a.hasOwnProperty('age')) //false
// 遍历对象,只遍历自身属性 结果:tom
for (const key in a) {
if (a.hasOwnProperty(key)) {
const element = a[key];
console.log(element)
}
}
// 遍历原型链,结果 :tom 22
for(const key in a) {
console.log(a[key])
}
1.15 使用call、apply借用原型链
/**
* obj 原型中有取得最大值的方法,
* 对象hi及其原型中没有这种方法,
* 现在让hi通过call或apply借用obj原型中的max()方法
*/
let obj = {
arr: [1, 141, 25, 6356, 1231]
}
// obj.__proto__ = {
// max() {
// return this.arr.sort((a, b) => b - a)[0]
// }
// }
// 这两种方式等价
Object.setPrototypeOf(obj, {
// 取得最大值
max() {
return this.arr.sort((a, b) => b - a)[0]
}
})
console.log(obj)
console.log(obj.max())
let hi = {
lessons: { js: 87, php: 63, node: 99, linux: 88 },
get arr() {
return Object.values(this.lessons)
}
}
console.log(obj.max.apply(hi))
// Math 中有max() , 让对象hi使用该方法
let max = Math.max(...[1, 141, 25, 6356, 1231])
console.log(max)
let hi = {
lessons: { js: 87, php: 63, node: 99, linux: 88 },
get arr() {
return Object.values(this.lessons)
}
}
console.log(hi)
console.log(hi.arr)
let himax = Math.max.call(...(hi.arr))
let himax2 = Math.max.apply(null,hi.arr)
console.log(himax) // 99
console.log(himax2) // 99
1.17 DOM节点借用Array原型方法
<button class="btn">按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
/**
* DOM节点借用Array原型方法
*/
let arr = [14,66,99,31,6]
let res = arr.filter(item => {
return item > 30
})
console.log(res) // [66, 99, 31]
// filter 在哪?
console.dir(Array) // 没有
console.log(Array.prototype) //有filter
console.dir(arr.__proto__) //有
console.dir([].__proto__) //有
// let btns = document.querySelectorAll('button') // 伪数组
// console.dir(btns)
// // 过滤btns找到带class的按钮
// let btnHasClass = btns.filter(item => {
// return item.hasAttribute('class')
// })
// console.dir(btnHasClass) //报错: btns.filter is not a function
// 由于数组中有这个方法,可以借用数组来完成目的
// let btns = document.querySelectorAll('button')
// let btnHasClass = Array.prototype.filter.call(btns,item => {
// return item.hasAttribute('class')
// })
// console.dir(btnHasClass) // 借用成功 0: button.btn
let btns = document.querySelectorAll('button')
let btnHasClass = [].filter.call(btns,item => {
return item.hasAttribute('class')
})
console.dir(btnHasClass) // 借用成功 0: button.btn
1.18 合理声明构造函数
/**
* 第一种方式: 属性、方法都写在构造函数中
* 缺点:每一个实例对象都有构造函数定义的属性和方法,由于方法是相同的,就造成不必要的内存资源浪费
*/
function Person(name) {
this.name = name
this.show = function() {
console.log(this.name)
}
}
let zs = new Person('张三')
let ls = new Person('李四')
console.dir(zs)
console.dir(ls)
/**
* 解决方式: 将属性定义在构造函数中,将方法定义在构造函数原型当中,
* 根据:实例化对象和构造函数共用一个原型
*/
function Person(name) {
this.name = name
}
Person.prototype.show = function() {
console.log(this.name)
}
let zs = new Person('张三')
let ls = new Person('李四')
console.dir(zs)
console.dir(ls)
zs.show() //张三
ls.show() //李四
1.20 不要滥用原型
<body>
<!-- 假设要实现点击按钮隐藏按钮的功能,
发现:没有这个函数,
报错:Uncaught TypeError: this.hide is not a function
想法:由于每个节点都是一个对象,在Object构造函数原型上创建这个方法
-->
<button class="btn" onclick="this.hide()">按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<script>
// 在没有第三方包引入情况下能够完成,
Object.prototype.hide = function() {
this.style.display = 'none'
}
console.dir(Object)
</script>
<!-- 引入第三方包,你发现完成的不是你的功能
这里第三方包hide方法完成的是其他功能
-->
<script src="./prototype.js"></script>
/**********************************************
// 第三方库 prototype.js
/**
* 由于第三方库中也有这个方法,就造成功能不稳定的情况
* 刚开始,可能能够运行,
* 由于项目当中会引入很多包,这些包与你定义的那些方法会产生耦合,会造成一些未知的情况出现
* 这会导致你的代码不健壮、不稳定
*/
Object.prototype.hide = function() {
this.style.background = 'red'
}
1.21 关于原型创建与获取
/**
* 最开始,js设置原型通过Object.create
* 缺点:只能设置不能获取
* 解决: 浏览器厂商自己研发 __proto__
*/
// function Person(name) {
// this.name = name
// }
let user = {
name: 'hi518',
show() {
console.log(this.name)
}
}
// 只能设置原型,不能获取
let zs = Object.create(user, {
name: {
value: '张三'
}
})
console.log(zs)
zs.show()
// __prototype__,能设置、能获取,但是非标准
let ls = { name: '李四' }
// 设置原型
ls.__proto__ = user
console.log(ls)
// 获取原型
console.log(ls.__proto__) //{name: "hi518", show: ƒ}
// setPrototypeOf与getPrototypeOf
let ww = { name: '王五' }
// 设置原型
Object.setPrototypeOf(ww, user)
console.log(ww)
// 获取原型
let pro = Object.getPrototypeOf(ww)
console.log(pro) //{name: "hi518", show: ƒ}
1.23 __proto__原来是属性访问器
- _proto_ 这个属性并不是严格意义的属性,它是一个getter或setter,会对测试的值进行判断的
/**
* __proto__ 是什么?
* __proto__这个属性并不是严格意义的属性,
* 它的本质是getter或setter,
* 并且他会对测试的值进行判断,判断是不是对象,是对象,能设置,否则不能设置
* __proto__怎么才能设置它为属性?
* __proto__是通过原型才有的,我们只需要把对象的原型设为null,就可以设置__proto__
*/
// let hd = { name: '后盾'}
// hd.__proto__ = {
// show() {
// console.log(this.name)
// }
// }
// hd.__proto__ = 99
// hd.show() //后盾 发现hd.__proto__ = 99并没有真正的修改__proto__
// __proto__原理
let hd = {
action: {},
get proto() {
return this.action
},
set proto(obj) {
if(obj instanceof Object) {
this.action = obj
}
}
}
hd.proto = {name:'eee'}
hd.proto = '123'
console.dir(hd.proto) // 在设置原型时只有是对象的才能设置成功
// let user = { name: 'user'}
// user.__proto__ = null
// let zs = { name: '张三' }
// zs.__proto__ = null
// zs.__proto__ = '123'
// console.log(zs.__proto__) // 123
// let ww = Object.create(null)
// ww.__proto__ = '111'
// console.log(ww.__proto__) // 111
2.继承
2.1 改变构造函数原型并不是继承
2.2 继承是原型的继承
- 第一种继承方式,改变父级
- 第二种继承方式,新增对象,新增的对象继承父级,改变构造函数的原型为新增对象
/**
* 改变构造函数的prototype并不是继承
* 改变构造函数原型之后,再在原型上添加方法,会添加到改变后的原型上
*/
function User() {}
User.prototype.name = function() {
console.log('user.name')
}
/**
* 第一种方式
* 改变原型的__proto__
*/
function Admin() {}
Admin.prototype.__proto__ = User.prototype
Admin.prototype.role = function() {
console.log('admin.role')
}
let hd = new Admin()
hd.role() // admin.role
/**
* 第二种方式
* 新增一个对象
* 相当于新建一个对象,这个对象的原型指向User.prototype,
* 这种方法改变了Admin.prototype,因此在此之前Admin.prototye中定义的方法会消失
* 注意:这种继承方式要在改变Admin.prototype定义方法,否则会报错
*/
// function Admin() {}
// Admin.prototype = Object.create(User.prototype)
// // 必须改变原型之后定义方法
// Admin.prototype.role = function() {
// console.log('admin.role')
// }
// let hd = new Admin()
// hd.role() // admin.role
function Member() {}
Member.prototype = User.prototype
Member.prototype.role = function() {
console.log('member.role')
}
- 使用第二种方式注意点
- 改变原型之后才能实例化对象或者添加方法
2.3 继承对constructor的影响
- 使用第二种方式完成的继承会丢失constructor
- 因此要加上 Admin.prototype.constuctor = Admin
- 加上的constructor是可遍历的
- 修改constructor,使它不可遍历
function User() {}
User.prototype.name = function() {
console.log('user.name')
}
/**
* 第二种方式
* 新增一个对象
* 相当于新建一个对象,这个对象的原型指向User.prototype,
* 这种方法改变了Admin.prototype,因此在此之前Admin.prototye中定义的方法会消失
* 注意:这种继承方式要在改变Admin.prototype定义方法,否则会报错
*/
function Admin() {}
Admin.prototype = Object.create(User.prototype)
/**
* 问题1:Admin.prototype少了constructor ?
* 解决:添加constructor
*/
Admin.prototype.constructor = Admin
//
/**
* 问题2:新增的constructor是可遍历的 ?
* 解决:设置属性constructor
*/
let a = Object.getOwnPropertyDescriptor(Admin.prototype,'constructor')
console.dir(a) //enumerable: true
// 修改constructor属性,使它不可遍历
Object.defineProperty(Admin.prototype,'constructor',{
value: Admin,
enumerable: false
})
// 必须改变原型之后定义方法和实例化对象
Admin.prototype.role = function() {
console.log('admin.role')
}
console.dir(Admin)
let hd = new Admin()
hd.role() // admin.role
// 设置后遍历中不再出现constructor
for (const key in Admin.prototype) {
console.log(key) // role name
}
2.4 方法重写与父级属性访问
- 父类中没有要用的方法
- 在自己原型中定义要用的方法
- 在自己原型中定义的方法当中,可以调用父类的方法
function User() {}
User.prototype.name = function() {
console.log('user.name')
}
User.prototype.site = function() {
return 'hello'
}
function Admin() {}
Admin.prototype = Object.create(User.prototype)
Admin.prototype.constructor = Admin
Object.defineProperty(Admin.prototype,'constructor',{
value: Admin,
enumerable: false
})
// 自己的方法
Admin.prototype.name = function() {
console.log('admin.name')
}
Admin.prototype.show = function() {
// 在自己的方法中引用父类的方法
console.log(User.prototype.site() +' '+ 'admin')
}
// 实例化对象
let hd = new Admin()
hd.name() //admin.name
hd.show() //hello admin
2.5 面向对象的多态实现
- 多态: 多个对象的原型相同,在原型当中调用对象的方法,会有不同的展现形式
function User() {}
User.prototype.show = function() {
console.log(this.description())
}
function Admin() {}
Admin.prototype = Object.create(User.prototype)
Admin.prototype.description = function() {
return '管理员在此'
}
function Member() {}
Member.prototype = Object.create(User.prototype)
Member.prototype.description = function() {
return '我是会员'
}
function Enterprise() {}
Enterprise.prototype = Object.create(User.prototype)
Enterprise.prototype.description = function() {
return '企业用户'
}
for (const obj of [new Admin(),new Member(),new Enterprise()]) {
obj.show() //管理员在此 我是会员 企业用户
}
2.6 使用父类构造函数初始属性
- 在构造函数中通过call或apply方法调用父类方法
function User(name, age) {
this.name = name
this.age = age
}
function Admin(name, age) {
User.call(this, name, age)
}
let hd = new Admin('张三', 18)
console.log(hd)
/**********************************************************/
function User(name, age) {
this.name = name
this.age = age
}
function Admin(...args) {
User.apply(this, args)
}
let hd = new Admin('张三', 18)
console.log(hd)
2.7 使用原型工厂封装继承
- 相当于把重复的代码封装为一个函数,通过函数完成继承
// 函数封装
function extend(fn1, fn2) {
if (fn1 instanceof Function && fn2 instanceof Function) {
fn1.prototype = Object.create(fn2.prototype)
Object.defineProperty(fn1.prototype, 'constructor', {
value: fn1,
enumerable: false
})
}
}
function User(name, age) {
this.name = name
this.age = age
}
User.prototype.show = function () {
console.log(this.name, this.age)
}
function Admin(name, age) {
User.call(this, name, age)
}
extend(Admin, User)
let hd = new Admin('张三', 18)
hd.show()
function Member(name, age) {
User.call(this, name, age)
}
extend(Member, User)
let ls = new Member('李四', 22)
ls.show()
function Enterprise(name, age) { }
2.8 对象工厂派生对象并实现继承
- 相当于封装一个函数返回一个对象
function User(name, age) {
this.name = name
this.age = age
}
User.prototype.show = function () {
console.log(this.name, this.age)
}
function admin(name, age) {
let obj = Object.create(User.prototype)
obj.role = function () {
console.log('admin role')
}
User.call(obj, name, age)
return obj
}
let zs = admin('张三', 18)
console.log(zs)
zs.show()
function member(name, age) {
let obj = Object.create(User.prototype)
User.call(obj, name, age)
return obj
}
let ls = member('李四', 22)
ls.show()
function enterprise(name, age) {
let obj = Object.create(User.prototype)
User.call(obj, name, age)
return obj
}
let ww = enterprise('王五', 80)
ww.show()
2.9 多继承造成的困扰
- js 没有多继承,只能通过继承父级,让父级再继承父级,…
- 问题:这种方式会让没有关系的混在一起,导致非常混乱
// 函数封装
function extend(fn1, fn2) {
if (fn1 instanceof Function && fn2 instanceof Function) {
fn1.prototype = Object.create(fn2.prototype)
Object.defineProperty(fn1.prototype, 'constructor', {
value: fn1,
enumerable: false
})
}
}
function User() {
Member.call(this)
}
function Member() {
Admin.call(this)
}
function Admin() { }
// Member 有积分功能,
extend(Member, Admin)
extend(User, Member)
User.prototype.address = function () {
console.log('收货地址')
}
Member.prototype.jifen = function () {
console.log('积分统计')
}
Admin.prototype.power = function () {
console.log('权限功能')
}
let zs = new User()
// 张三使用 自身的收货地址功能、积分功能、权限功能 需要让User继承Member,让Member继承Admin
zs.address()
zs.jifen()
zs.power()
2.10 使用mixin实现多继承
- mixin 混合功能
- 理解:有一个类,这个类为其他类提供服务,但是提供服务的手段不是继承
- 步骤:
- 将构造函数改为对象
- 向原型中压入对象属性
function User() { }
User.prototype.address = function () {
console.log('收货地址')
}
const member = {
jifen() {
console.log('积分统计')
}
}
const admin = {
power() {
console.log('权限功能')
}
}
// 直接向User原型中压入对象属性
// User.prototype.jifen = member.jifen
// User.prototype.power = admin.power
// console.log(User.prototype)
// let zs = new User()
// zs.address()
// zs.jifen()
// zs.power()
// 当对象中有很多可枚举属性时,可以使用Object.assign
Object.assign(User.prototype, member, admin)
console.log(User.prototype)
let zs = new User()
zs.address()
zs.jifen()
zs.power()
2.11 mixin内部继承与super关键字
- 通过改变对象内部的__proto__实现
function User() { }
User.prototype.address = function () {
console.log('收货地址')
}
const request = {
ajax() {
return '请求'
}
}
const member = {
__proto__: request,
jifen() {
console.log(this.__proto__.ajax() + '积分统计')
// super = this.__proto__
// super 是对象的原型
// console.log(super.ajax() + '积分统计')
}
}
const admin = {
power() {
console.log('权限功能')
}
}
// 当对象中有很多可枚举属性时,可以使用Object.assign
Object.assign(User.prototype, request, member, admin)
console.log(User.prototype)
let zs = new User()
zs.address()
zs.jifen()
zs.power()
// User.prototype.ajax = request.ajax
// User.prototype.jifen = member.jifen
// User.prototype.power = admin.power
// console.log(User.prototype)
// let zs = new User()
// zs.address()
// zs.jifen()
// zs.power()
3.继承的几种方式
3.1原型链
-
基本思想: 利用原型让一个引用类型继承另一个引用类型的属性与方法
-
原型链定义:实例与原型的链条
-
问题:
- 包含引用类型值的原型与实例对象会共用同一个引用类型
- 在创建子类型的实例时,不能像超类型的构造函数传递参数
3.2 经典继承
-
基本思想: 在子类型构造函数的内部调用超类型构造函数------ call() 、apply()
-
优点: 能够在子类型构造函数中向超类型构造函数传递参数
-
缺点:方法都在构造函数中定义,函数无法复用
3.3 组合继承
- 基本思想:使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承
3.4 原型式继承
- 基本思想:借助原型可以基于已有的对象创建新对象
- 特点:包含引用类型值得属性始终都会共享相应的值,就像使用原型模式一样
3.5 寄生式继承
- 基本思路:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后返回对象
3.6 寄生组合式继承
- 概念:通过借用构造函数来继承属性,通过原型链的混合形式来继承方法
- 基本思路: 不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型的原型的一个副本而已
4.创建对象的几种模式
4.1 工厂模式
- 优点:创建多个相似对象
- 问题:没有解决对象识别的问题(即怎样知道一个对象的类型)
/**
* 工厂模式
*/
function createPerson(name, age, job){
var o = new Object()
o.name = name
o.age = age
o.job = job
o.sayName = function() {
console.log(this.name)
}
return o
}
var person1 = createPerson('张三',18,'software Engineer')
var person2 = createPerson('red',31,'teacher')
4.2 构造函数模式
- 优点:可以创建特定类型的对象,可以将他的实例标识为一种特定的类型
- 检测对象类型使用 instanceof
- 缺点:每个方法都会在每个实例上重新创建一边
/**
* 构造函数模式
*/
function Person(name,age,job) {
this.name = name
this.age = age
this.job = job
this.sayName = function() {
console.log(this.name)
}
}
var person1 = new Person('张三', 18, 'software Engineer')
var person2 = new Person('red', 31, 'teacher')
4.3 原型模式
- 优点:所有对象实例共享它所包含的属性和方法
- 缺点:对于引用类型值得属性,所有实例会共享
/**
* 原型模式
*/
function Person() {}
Person.prototype.name = '张三'
Person.prototype.age = 18
Person.prototype.job = 'software Engineer'
Person.prototype.sayName = function() {
console.log(this.name)
}
var person1 = new Person()
person1.sayName() //张三
var person2 = new Person()
person2.sayName() //张三
4.4 组合使用构造函数模式和原型模式
- 概念:构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性
- 特点:
- 每个实例都会有自己的一份实例属性的副本,同时又共享着对方法的引用,最大限度的节省了内存
- 这种混合模式还支持向构造函数传递参数
/**
* 混合模式
*/
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
this.friends = ['Tom', 'Red']
}
Person.prototype = {
constructor: Person,
sayName() {
console.log(this.name)
}
}
var person1 = new Person('张三', 18, 'software Engineer')
var person2 = new Person('李四',31,'teacher')
person1.friends.push('Van')
console.log(person1.friends) // ["Tom", "Red", "Van"]
console.log(person2.friends) // ["Tom", "Red"]
console.log(person1.friends === person2.friends) // false
console.log(person1.sayName === person2.sayName) // true
4.5 动态原型模式
- 相当于在构造函数中加了条件,过滤掉已经存在的方法
/**
* 动态原型模式
*/
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
this.friends = ['Tom', 'Red']
// 方法
if(typeof this.sayName != 'function') {
Person.prototype.sayName = function() {
console.log(this.name)
}
}
}
var person1 = new Person('张三', 18, 'software Engineer')
var person2 = new Person('李四',31,'teacher')
person1.friends.push('Van')
console.log(person1.friends) // ["Tom", "Red", "Van"]
console.log(person2.friends) // ["Tom", "Red"]
console.log(person1.friends === person2.friends) // false
console.log(person1.sayName === person2.sayName) // true
// 在实例化之后就已经加上了相应的方法
console.dir(Person.prototype) // 里面已经有了 sayName: ƒ ()
person1.sayName() // 张三
person2.sayName() // 李四
4.6 寄生构造函数
- 相当于复制内置对象,在添加相应方法
/**
* 寄生构造函数模式
*/
function Person(name, age, job) {
var o = new Object()
o.name = name
o.age = age
o.job = job
o.friends = ['Tom', 'Red']
o.sayName = function() {
console.log(o.name)
}
return o
}
var person1 = new Person('张三', 18, 'software Engineer')
var person2 = new Person('李四',31,'teacher')
person1.friends.push('Van')
console.log(person1.friends) // ["Tom", "Red", "Van"]
console.log(person2.friends) // ["Tom", "Red"]
console.log(person1.friends === person2.friends) // false
console.log(person1.sayName === person2.sayName) // false
/**
* 例子
*/
function SpecialArray() {
// 创建数组
var value = new Array()
// 添加值
value.push.apply(value, arguments)
// 添加方法
value.toPipedString = function() {
return this.join('|')
}
// 返回数组
return value
}
var color = new SpecialArray('red','orange','yellow','green')
console.log(color) // ["red", "orange", "yellow", "green", toPipedString: ƒ]
var a= color.toPipedString()
console.log(a) // red|orange|yellow|green
4.7 稳妥构造函数模式
- 特点:
- 没有公共属性
- 只能通过已有方法访问原有数据(属性)
/**
* 稳妥构造函数模式
*/
function Person(name, age, job) {
// 创建要返回的对象
var o = new Object()
// 可以在这里定义私有变量和函数
// 添加方法
o.sayName = function() {
console.log(o.name)
}
// 返回对象
return o
}
var person1 = new Person('张三', 18, 'software Engineer')
// 只能通过sayName访问name属性
person1.sayName()
var person2 = new Person('李四',31,'teacher')