面向对象拓展

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('李四');
特点
  1. 构造函数的函数名的第一个字母通常大写

  2. 函数体内使用this关键字,代表所要生成的对象实例

  3. 生成对象的时候,必须使用new命令来调用构造函数(否则this会发生变化,指向window)

  4. 通常来说构造函数的结尾不会出现return关键字,当出现return且return的是一个对象时,构造函数返回的实例以return的结果为准

  5. 所有的实例对象都可以拥有构造函数中的属性和方法。但是,实例对象独享模板中的属性和方法,而不是共享

    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

缺点:

  1. 引用类型的属性被所有实例共享
  2. 在创建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

优点:

  1. 避免了引用类型的属性被所有实例共享
  2. 可以在实例化子函数时中向父函数传参

缺点:

  1. 父类并不在原型链上,不存在原型关系
  2. 无法继承原型方法,只能继承属性和实例方法
组合继承

组合原型链继承和借用构造函数继承,使用原型链实现对原型方法的继承,而通过借用构造函数来实现对实例属性的继承

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

缺点:

  1. 引用类型的属性被所有实例共享
  2. 属性被放在了原型上,而非实例上
  3. 在创建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

优点:

  1. 避免了引用类型的属性被所有实例共享
  2. 可以在实例化子函数时中向父函数传参

缺点:

  1. 父类并不在原型链上,不存在原型关系
  2. 无法继承原型方法,只能继承属性和实例方法
组合继承(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 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值