1.call、apply、bind
函数的三个原型方法都可以修改函数内部this的指向
- call:修改函数this的指向,直接调用函数,call方法的参数列表是普通参数列表
- apply:修改函数this的指向,直接调用函数,apply方法的参数列表是参数列表组成的数组(可以将数组变成实参列表)
- bind:修改函数this的指向,不可以直接调用函数,返回一个修改后的函数
function fn(x, y) {
console.log(x + y)
}
fn(2, 3); // 5
fn.call(null, 2, 3); // 5
fn.apply(null, [2, 3]); // 5
fn.bind(null, 2, 3)(); // 5
var arr = [1, 2, 3, 4];
var max = Math.max.apply(null, arr); // 获取数组的最大值
var arr = [1, 2, 3, 4]
function fun(x, y, z) {
console.log(x+y+z,this)
}
fun(10,20,30) // 60 this指向window
fun.call(arr, 10,20,30) // 60 this指向arr
fun.apply(arr,arr) // 6 this指向arr
fun.apply(arr,[10,20,30]) // 60 this指向arr
fun.bind(arr, 10, 20, 30)() // 60 this指向arr
2.this的使用
this只能存在于函数中
this关键字,随着自身所在的位置不同,它所代表的意义也不相同,其核心思想是:方法属于谁,this就指向谁,常见的this有四种指向
普通函数和回调函数的this指向window
普通函数的this指向的是window,回调函数的this指向的是window,不管处在哪种环境下,结果都是window(严格模式下或者ES6中,this指向undefined)
// 普通函数指向window
function fn() {
console.log(this); // window
}
// 回调函数指向window
setTimeout(function () {
console.log(this); // window
}, 1000);
构造函数中的this指向构造函数实例
function A() {
this.name = '张三'; // 实例
}
var a = new A();
指向对象本身
对象中的this指向的是函数所在的对象本身,事件函数中this都指向事件所绑定的事件源
// 对象中函数指向对象本身
var o = {
say: function () {
console.log(this); // o
}
}
// 事件处理函数中指向绑定的事件源
document.body.onclick = function () {
console.log(this); // <body>...</body>
}
指向call/apply的第一个参数
箭头函数会修改函数内部this的指向,内部的this指向函数外部this的指向
document.onclick = () => { console.log(this) // 因为是箭头函数,this 指向 window,而不是document }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mkFQa551-1596372465093)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200728101735990.png)]
3.构造函数(类)
概念
JS中没有类的概念,类是一种用户定义的引用数据类型,JS中用构造函数来模拟类。所谓构造函数,就是提供一个生成对象的模板,并描述对象的基本结构的函数。通过一个构造函数,可以生成多个实例对象,每个实例对象都有相同的结构(构造函数:给实例写属性的模板)
function Person(name) {
this.name = name;
this.type = 'Chinese';
this.hair = 'black';
this.say = function() { // 实例方法会在每个实例上创建,占用内存
console.log('I can say');
}
}
var p1 = new Person('张三');
var p2 = new Person('李四');
特点
-
构造函数的函数名的第一个字母通常大写
-
函数体内使用this关键字,代表所要生成的对象实例
-
生成对象的时候,必须使用new命令来调用构造函数(否则this会发生变化,指向window)
-
通常来说构造函数的结尾不会出现return关键字,当出现return且return的是一个对象时,构造函数返回的实例以return的结果为准
-
所有的实例对象都可以拥有构造函数中的属性和方法。但是,实例对象独享模板中的属性和方法,而不是共享
function Person(name,age) { this.name = name this.hair = 'black' this.age = age this.say = function () { console.log(666) } } var p1 = new Person('rose',18) var p2 = new Person('Jack',18) console.log(p1.say === p2.say) // false
类(构造函数)是对象(实例)的抽象化,对象(实例)是类(构造函数)的具象化、实例化
4.原型 prototype
为了解决构造函数的对象实例之间无法共享属性,创建多个实例对象内存占用过大的问题,js提供了prototype(原型对象)来解决,原型对象的作用就是定义所有对象实例所共享的属性和方法,共享的方法只会被定义一次,所有实例对象共享而不是单独创建
function Person(name) {
this.name = name;
this.type = 'Chinese';
this.hair = 'black';
}
Person.prototype.say = function() { // 原型方法方法会被每个实例共享
console.log('I can say');
}
var p = new Person('张三');
console.log(p.__proto__ === Person.prototype); // true
实例对象的原型和构造函数的原型是同一个原型
function Person(name,age) { this.name = name this.hair = 'black' this.age = age } Person.prototype.say = function () { console.log(this.name) } var p1 = new Person('rose',18) var p2 = new Person('Jack',18) console.log(p1.say === p2.say) // true // 实例对象的原型和构造函数的原型是同一个原型 console.log(p1.__proto__ === Person.prototype) // true console.log(p2.__proto__ === Person.prototype) // true console.log(p1.hasOwnProperty('say')) // false console.log(p1.__proto__.hasOwnProperty('say')) // true console.log(Person.prototype.hasOwnProperty('say')) // true
5.实例方法、静态方法、原型方法
-
实例方法就是只有实例可以调用
-
静态方法只有构造函数可以调用
-
原型方法是实例和构造函数都可以调用,是共享的方法
function Person() {
this.say = function () {
console.log('实例方法');
}
}
Person.say = function () {
console.log('静态方法');
}
Person.prototype.say = function () {
console.log('原型方法');
}
// 实例方法(直接绑在实例上的方法)
function Person() {
this.name = 'jack'
// 实例方法
this.say = function () {
console.log(this.name)
}
}
var p = new Person()
// 实例调用
p.say() // jack
// 静态方法(直接写在构造函数上的方法,只能构造函数用)
function Person() {
this.name = 'jack'
this.say = function () {
console.log(this.name)
}
}
// 静态方法,直接写在构造函数上(run 方法挂在了 Person 身上)
Person.run = function () { // 此处把 Person 当做对象来处理
console.log(666)
}
var p = new Person()
// 构造函数调用
Person.run() // 666
// 原型方法
function Person() {
this.name = 'jack'
this.say = function () {
console.log(this.name)
}
}
// 原型方法(写在了构造函数的原型上,也写在了实例的原型上)
Person.prototype.speak = function () { // 此处把 Person 当做对象来处理
console.log(this.name)
}
var p = new Person()
// 实例调用
p.speak() // jack
// 构造函数调用(在原型方法中,一般不用构造函数调用)
Person.prototype.speak() // undefined
6.扩展原型方法
Array扩展min、max、pop方法
var arr = [1, 3, 5, 7, 9]
Array.prototype.max = function () {
return Math.max(...this);
}
Array.prototype.min = function () {
return Math.min(...this);
}
var min = arr.min()
var max = arr.max()
console.log(arr) // [1, 3, 5, 7, 9]
console.log(min, max) // 1 9
Date扩展format方法
// 给 Date 的原型对象定义一个 format 属性,然后给一个描述对象
Object.defineProperty(Date.prototype, 'format', {
configurable: true, // 可以删除
writable: true, // 可以修改
enumerable: false, // 不可枚举
value: function format(fmt) { // value 属性 -> 函数
// 获取所有的时间
var year = this.getFullYear(); // 拿到 年
var month = this.getMonth() + 1; // 拿到 月
var day = this.getDate(); // 拿到 天
var hours = this.getHours(); // 拿到 小时
var minutes = this.getMinutes(); // 拿到 分钟
var seconds = this.getSeconds(); // 拿到 秒钟
// 格式化时间
return fmt.replace('YYYY', year) // replace 有返回值,返回的是一个字符串
.replace('MM', month > 9 ? month : '0' + month)
.replace('DD', day > 9 ? day : `0${day}`)
.replace('hh', hours > 9 ? hours : `0${hours}`)
.replace('mm', minutes > 9 ? minutes : `0${minutes}`)
.replace('ss', seconds > 9 ? seconds : `0${seconds}`);
}
});
var date = new Date()
var res = date.format('MM/DD/YYYY hh:mm:ss');
console.log(res); // 07/29/2020 11:57:37
Object的原型是所有对象的原型,Object函数是所有的构造函数的父级,所有Object上的原型方法被所有对象共享
Object.prototype.say = function() { console.log(666); }
new Date().say(); // 666
pop方法
var arr = Array(1, 2, 3, 4)
console.log(arr.__proto__.hasOwnProperty('pop')) // true
arr.pop() // 删除数组中的最后一个数据4,此时arr=[1,2,3]
console.log(arr) // [1, 2, 3]
console.log(arr.pop) // ƒ pop() { [native code] }
console.log(arr.pop()) // 3 -> 删除数组中的最后一个数据3,此时arr=[1,2]
console.log(arr) // [1, 2]
7.ES6类
ES6的语法中,提出了类的概念,用class关键字定义类
class Person {
constructor(name) { // 构造器函数声明属性
this.name = name; // 定义属性
this.type = 'Chinese'; // 定义属性
this.hair = 'black'; // 定义属性
}
speak = function () { // 实例方法
console.log('I can speak');
}
say() { // 原型方法
console.log('I can say');
}
}
var p = new Person('张三');
class Person {
constructor(name,type,sex) {
this.name = name
this.type = type
this.sex = sex
}
say() { // 原型方法
console.log(`我叫${this.name},是${this.sex}生`)
}
speak = function () { // 实例方法
console.log(`我是${this.type}人`)
}
}
var s = new Person('丁丁',null,'女')
s.say()
var sp = new Person(null,'中国',null)
sp.speak()
8.constructor
构造器函数,构造方法,一个对象实例的构造器函数指向的是创建这个对象的构造函数(指的是:最近的构造函数,不包含原型链中的父函数)
var arr = new Array();
console.log(arr.constructor === Array); // true
console.log(arr.constructor === Object); // false
9.instanceof
判断实例对象是否来自于某个构造函数(包含原型链中的父函数)
var arr = new Array();
console.log(arr instanceof Array); // 判断arr是否来自于Array
console.log(arr instanceof Object); // 判断arr是否来自于Object
10.面向对象继承
原型链继承
通过原型链完成继承
function Person(age, sex) {
this.name = '人类';
this.age = age;
this.sex = sex;
}
Person.prototype.say = function () {
console.log(this.name);
}
function Chinese() {
this.name = '中国人';
}
Chinese.prototype = new Person(18, 'male');
var c = new Chinese();
c.say(); // 中国人
console.log(c.age); // 18
console.log(c.sex); // male
缺点:
- 引用类型的属性被所有实例共享
- 在创建Chinese的实例时,不能向Person传参
借用构造函数继承(经典继承)
通过复制父类构造函数内的属性完成属性的继承
function Person(age, sex) {
this.name = '人类';
this.age = age;
this.sex = sex;
}
Person.prototype.say = function () {
console.log(this.age);
}
function Chinese(age, sex) {
Person.call(this, age, sex);
}
var c = new Chinese(18, 'male');
c.say(); // 18
优点:
- 避免了引用类型的属性被所有实例共享
- 可以在实例化子函数时中向父函数传参
缺点:
- 父类并不在原型链上,不存在原型关系
- 无法继承原型方法,只能继承属性和实例方法
组合继承
组合原型链继承和借用构造函数继承,使用原型链实现对原型方法的继承,而通过借用构造函数来实现对实例属性的继承
function Person(age, sex) {
this.name = '人类';
this.age = age;
this.sex = sex;
}
Person.prototype.say = function () {
console.log(this.name);
}
function Chinese(age, sex) {
Person.call(this, age, sex);
this.hair = 'black';
}
Chinese.prototype = new Person();
var c = new Chinese(18, 'male');
c.say(); // 18
优点:融合原型链继承和构造函数的优点,是JavaScript中最常用的继承模式
缺点:调用了两次父类构造函数(组合继承最大的问题是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部)
原型式继承
原型式继承本质其实就是个浅拷贝,以一个对象为模板复制出新的对象
var o = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
}
// ES5原型继承
function create(o) {
var G = function () { };
G.prototype = o;
return new G();
}
var me = create(o);
// ES6原型继承
var me = Object.create(o);
寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。可以理解为在原型式继承的基础上新增一些函数或属性
// 寄生式继承 可以理解为在原型式继承的基础上增加一些函数或属性
var o = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
}
function create(o) {
var G = function () { };
G.prototype = o;
return new G();
}
// var me = create(o);
function CreateObj(o) {
var newobj = create(o); // 创建对象
newobj.say = function () { // 增强对象
console.log(this.name);
}
return newobj; // 返回对象
}
var p = CreateObj(o);
p.say(); // Nicholas
寄生组合式继承
function create(o) {
var G = function () { };
G.prototype = o;
return new G();
}
ES6继承
通过ES6语法完成继承
// 父类
class Person {
constructor(age, sex) {
this.name = '人类';
this.age = age;
this.sex = sex;
}
say() {
console.log(this.age);
}
}
// 子类
class Chinese extends Person {
constructor(age, sex) { // 当没有新增属性时,可以不写constructor函数
super(age, sex); // super 继承属性
this.name = '中国人'; // 当新增属性时,必须写完整的constructor,否则无法完成继承
this.color = 'gray';
}
speak() {
console.log(this.name);
}
}
var c = new Chinese(18, 'male');
c.say(); // 18
c.speak(); // 中国人
11.链式调用
function Person() {
this.name = '张三';
}
Person.prototype.say = function say() {
console.log('说');
return this; // 函数执行完毕后,返回实例对象
}
Person.prototype.eat = function eat() {
console.log('吃');
return this;
}
Person.prototype.run = function run() {
console.log('跑');
return this;
}
var p = new Person()
console.log(p.say() === p); // true
p.say().eat().run(); // 说 吃 跑
function Person() {
this.name = 'jack'
}
Person.prototype.eat = function eat() {
console.log('吃饭')
return this
}
Person.prototype.sleep = function sleep() {
console.log('睡觉')
return this
}
Person.prototype.play = function play() {
console.log('打豆豆')
return this
}
var p = new Person()
p.eat().sleep().play()
12.面向对象轮播图
function Cal(imgs, container) {
this.oImgs = null;
this.oItems = null;
this.index = 0;
this.timer = null;
this.imgs = imgs;
this.container = container;
this.init();
}
Cal.prototype.init = function () {
var oContent = document.createElement('div');
oContent.className = 'content';
var oPagation = document.createElement('div');
oPagation.style.cssText = `
position: absolute;
right: 40px;
bottom: 20px;
display: flex;
`
var content = '';
var pagation = '';
this.imgs.forEach((img, index) => {
content += `<img style='width: 500px; height: 300px; display: ${index === 0 ? 'block' : 'none'};' src="${img}" alt="">`;
pagation += `<div style='width: 14px; height: 14px; border-radius: 50%; background-color: ${index === 0 ? '#f00' : '#999'}; margin: 0 5px;' class="item"></div>`;
});
oContent.innerHTML = content;
oPagation.innerHTML = pagation;
this.container.appendChild(oContent);
this.container.appendChild(oPagation);
this.oImgs = this.container.querySelectorAll('.content img')
this.oItems = this.container.querySelectorAll('.item')
this.start();
}
Cal.prototype.start = function () {
this.timer = setInterval(() => {
this.index++;
if (this.index > this.oItems.length - 1) {
this.index = 0;
}
this.active();
}, 2000)
}
Cal.prototype.active = function () {
Array.from(this.oItems).forEach((item, index) => {
this.oItems[index].style.backgroundColor = '#999999';
this.oImgs[index].style.display = 'none';
});
this.oItems[this.index].style.backgroundColor = '#f00';
this.oImgs[this.index].style.display = 'block';
}
13.面向对象操作元素
将元素封装成一个特殊的对象,对象拥有方法,通过调用方法达到对元素的操作
获取元素
// 通过构造函数获取元素
function GetEle(selector) {
if (typeof selector === 'string') {
this.eles = document.querySelectorAll(selector);
} else {
this.eles = [selector];
}
}
// 封装函数
// 将获取的元素 通过 seletor 选择器 封装成一个特殊的对象
function $(selector) {
return new GetEle(selector);
}
隐藏
GetEle.prototype.hide = function () {
Array.from(this.eles).forEach(item => item.style.display = 'none');
return this;
}
显示
GetEle.prototype.show = function () {
Array.from(this.eles).forEach(item => item.style.display = 'block');
return this;
}
点击
GetEle.prototype.click = function (callback) {
Array.from(this.eles).forEach(item => {
item.onclick = function (e) {
var e = event || e
callback(this,e)
}
})
return this;
}
事件委托
// type 事件类型
// callback 回调
// selector 选择器
GetEle.prototype.on = function (type, selector,callback) {
Array.from(this.eles).forEach(item => {
item.addEventListener(type,function (e) {
var e = event || e
var target = e.target || e.srcElement
if (Array.from(item.querySelectorAll(selector)).includes(target)) {
callback && callback()
}
})
})
return this
}
非表单元素内容
GetEle.prototype.html = function (content) {
if (!content && content != '') { // 表示用户没有传,或者传进来的不是一个空串,则认为是在:读
return this.eles[0].innerHTML // 一般是读第一个
} else { // 用户传了,则认为在:改
Array.from(this.eles).forEach(item => {
item.innerHTML = content
})
}
return this;
}
表单元素内容
GetEle.prototype.text = function (content) {
if (!content && content != '') { // 表示用户没有传,或者传进来的不是一个空串,则认为是在:读
return this.eles[0].innerText // 一般是读第一个
} else { // 用户传了,则认为在:改
Array.from(this.eles).forEach(item => {
item.innerText = content
})
}
return this;
}
文本框的内容
GetEle.prototype.val = function (content) {
if (!content && content != '') { // 表示用户没有传,或者传进来的不是一个空串,则认为是在:读
return this.eles[0].value // 一般是读第一个
} else { // 用户传了,则认为在:改
Array.from(this.eles).forEach(item => {
item.value = content
})
}
return this;
}
属性
GetEle.prototype.attr = function (attr, value) {
if (value === undefined) { // 读
return this.eles[0].getAttribute(attr)
} else { // 写
Array.from(this.eles).forEach( item => item.setAttribute(attr, value))
}
return this
}
多个属性
GetEle.prototype.attrs = function (attr, value) {
if (value === undefined) { // 读
if (typeof attr === 'string') {
return this.eles[0].getAttribute(attr)
} else {
Array.from(this.eles).forEach(item => {
// 循环遍历atrr
Object.keys(attr).forEach(name => item.setAttribute(name, attr[name]))
})
}
} else { // 写
Array.from(this.eles).forEach( item => item.setAttribute(attr, value))
}
return this
}
样式
GetEle.prototype.css = function (key,value) {
if (value === undefined) { // 读
if (key.constructor === String) {
return getComputedStyle(this.eles[0])[key]
} else {
Array.from(this.eles).forEach( item => {
Object.keys(key).forEach(name => {
item.style[name] = key[name]
})
})
}
}else { // 写
Array.from(this.eles).forEach( item => item.style[key] = value)
}
return this
}
指定下标
GetEle.prototype.eq = function (index) {
this.eles = [this.eles[index]];
return this;
}
获取指定下标
GetEle.prototype.index = function () {
var parent = this.eles[0].parentNode // 获取父元素
var children = parent.children // 获取所有的子元素
var i = 0
// item 指所有子元素中 单个的 子元素
Array.from(children).forEach((item, index) => { // 遍历children
if (item === this.eles[0]) i = index
})
return i
}
获取指定下标的其他兄弟元素
// 方法 一
GetEle.prototype.siblings = function () {
var parent = this.eles[0].parentNode
var children = parent.children
var list = []
Array.from(children).forEach(item => {
if (item !== this.eles[0]) list.push(item)
})
this.eles = list
return this
}
// 方法二
GetEle.prototype.siblings = function () {
var parent = this.eles[0].parentNode
var children = parent.children
this.eles = Array.from(children).filter(item => item !== this.eles[0])
return this
}
14.设计模式
什么是设计模式
设计模式是我们每天编程遇到的问题的可重用解决方案。设计模式主要是为了解决对象的生成和整合问题。换句话说,设计模式可以作为可应用于现实世界编程问题的模板
设计模式分类
根据实际应用中遇到的不同问题,将模式分为三种类型
- 创建型模式
- 结构型模式
- 行为型模式
创建型模式
这类模式用于对象的生成和生命周期的管理。
创造模式可以决定生成哪些对象,提高了程序的灵活性。模式如下:
- 抽象工厂模式
- 生成器模式
- 工厂方法模式
- 单例模式:只能出现一次
- 原型模式
结构型模式
这类模式描述了向现有对象添加功能的不同方式,简单地说,这个模式着重于解耦对象的接口实现模式如下:
- 适配器模式
- 桥接模式
- 组合模式
- 装饰者模式
- 外观模式
- 享元模式
- 代理模式
行为型模式
- 责任链模式
- 命令模式
- 解释器模式
- 迭代器模式
- 中介者模式
- 备忘录模式
- 观察者模式
- 状态模式
- 策略模式
- 模板方法模式
- 访问者模式
设计模式中没有MVC,MVP,MVVM这些是架构方式,也没有爆破模式,飞行模式
15.面向对象继承
ES6继承
继承:子类继承父类(继承和构造函数相关)
通过ES6语法完成继承(掌握)
// 父类
class Person { // 人类
constructor(age, sex) { // 构造器(构造函数)
this.name = '人类';
this.age = age;
this.sex = sex;
}
// 方法继承
say() {
console.log(this.age);
}
}
// 子类
// extends 继承,即:Chinese 继承 Person
class Chinese extends Person { // 中国人
// constructor 和 super 缺一不可,且 super 必须写在第一行
constructor(age, sex) { // 当没有新增属性时,可以不写constructor函数
// super 的作用是:给父类传参
super(age, sex); // super 继承属性
this.name = '中国人'; // 当新增属性时,必须写完整的constructor,否则无法完成继承
this.color = 'gray';
}
// 方法继承
speak() {
console.log(this.name);
}
}
var c = new Chinese(18, 'male');
// 方法继承的调用
c.say(); // 18
c.speak(); // 中国人
原型链继承(ES5)
通过原型链完成继承(核心:将父类的实例变成了子类的原型)
// 父类
function Person(age, sex) {
this.name = '人类';
this.age = age;
this.sex = sex;
}
Person.prototype.say = function () {
console.log(this.name);
}
// 子类
function Chinese() {
this.name = '中国人';
}
// 用 原型链 让 子类 继承 父类
Chinese.prototype = new Person(18, 'male');
var c = new Chinese();
c.say(); // 中国人
console.log(c.age); // 18
console.log(c.sex); // male
缺点:
- 引用类型的属性被所有实例共享
- 属性被放在了原型上,而非实例上
- 在创建Chinese的实例时,不能向Person传参(因此只能继承一些固定的属性)
借用父构造函数继承(经典继承(ES5))
通过复制父类构造函数内的属性完成属性的继承
// 父类
function Person(age, sex) {
this.name = '人类';
this.age = age;
this.sex = sex;
}
// 方法
Person.prototype.say = function () {
console.log(this.age);
}
// 子类
// 父构造函数继承的核心代码
// 继承的是属性
function Chinese(age, sex) {
// this 指向 c
Person.call(this, age, sex); // 完成了复制的操作
this.hair = 'black'
}
var c = new Chinese(18, 'male');
console.log(c instanceof Chinese) // true
console.log(c instanceof Person) // false
c.say(); // 报错:c.say is not a function
优点:
- 避免了引用类型的属性被所有实例共享
- 可以在实例化子函数时中向父函数传参
缺点:
- 父类并不在原型链上,不存在原型关系
- 无法继承原型方法,只能继承属性和实例方法
组合继承(ES5)
组合原型链继承和借用构造函数继承,使用原型链实现对原型方法的继承,而通过借用构造函数来实现对实例属性的继承
// 父类
function Person(age, sex) {
this.name = '人类';
this.age = age;
this.sex = sex;
}
// 方法
Person.prototype.say = function () {
console.log(this.name);
}
// 子类
function Chinese(age, sex) {
Person.call(this, age, sex);
this.hair = 'black';
}
// 用 原型链 让 子类 继承 父类
// 继承的是方法
Chinese.prototype = new Person();
var c = new Chinese(18, 'male');
c.say(); // 人类
console.log(c instanceof Chinese) // true
console.log(c instanceof Person) // true
优点:融合原型链继承和构造函数的优点,是JavaScript中最常用的继承模式
缺点:调用了两次父类构造函数(组合继承最大的问题是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部)
原型式继承
原型式继承本质其实就是个浅拷贝,以一个对象为模板复制出新的对象
var o = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
}
// ES5原型继承
function create(o) {
var G = function () { };
G.prototype = o;
return new G();
}
var me = create(o);
// ES6原型继承
var me = Object.create(o);
缺点:在继承的过程中,子类不可以创建新的方法和新的属性
寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。可以理解为在原型式继承的基础上新增一些函数或属性
// 寄生式继承 可以理解为在原型式继承的基础上增加一些函数或属性
var o = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
}
function create(o) {
var G = function () { };
G.prototype = o;
return new G();
}
// var me = create(o);
function CreateObj(o) {
var newobj = create(o); // 创建对象
newobj.say = function () { // 增强对象
console.log(this.name);
}
return newobj; // 返回对象
}
var p = CreateObj(o);
p.say(); // Nicholas