封装和继承

1.封装和继承

​ i 封装

​ 为了隐藏细节

​ ii 继承

​ 为了复用代码

​ iii 多态

​ 灵活

####1.1原型链(对象与对象之间的关系)

1
2
var obj1 = {name:'1'};
window.Object.prototype === obj.__proto__; //true

原型链

上面的原型链图形象的表示了他们之间的关系,说白了就是大家都共用同一个 “ 仓库” , 所有对象都共用 Object的原型,他们各自又有自己的 “ 仓库”

arr1.__proto__ === window.Array.prototype 自己的 Array 仓库,里面有 join ,splice 等方法

arr1.__proto__.__proto__ === window.Object.prototype Object的仓库,里面有 toString , valueOf 等方法

Object 是所有对象的最底层的原型。

1.2this(对象与函数之间的关系)

函数: 可执行代码组成的一种对象

所有使用 function声明的函数都只有两个参数 this 和 arguments,

一定要记住

1.参数的值只有传参的时候才能确定

2.this 是call()的第一个参数

=> this 的值只有在传参的时候才能确定

1
2
3
4
5
6
7
8
9
10
11
12
13
var obj = {
name:'mayun',
child:{
name:'sicong',
sayName:function(){console.log(this.name)}
}
}

obj.child.sayName() //sicong this === obj.child 就是函数的 点 前面的所有
obj.child.sayName.call(obj) // mayun 因为 this === obj obj.name === 'mayun'
//因此我们也可以看出 function(){} 跟这个 sayName 一点关系都没有, sayName 只是存了 function(){} 的地址而已
//更直观点
obj.child.sayName.call({name:'wtf'}) //wtf

来几个题试试

1
2
3
4
5
6
7
8
9
function f(){ console.log(this) } 
function f(){ console.log(this) } f()
function f(){ ‘use strict’ console.log(this) } f()
var obj = {sayThis:f} ; obj.sayThis()
var obj = {sayThis:f} ; obj.sayThis.call()
button.onclick = function(){console.log(this)}
$('#test').on('click',function(){console.log(this)})
$('#ul1').on('click','li',function(){console.log(this)})
varvm = new Vue({data:function(){console.log(this)}})

答案

1
2
3
4
5
6
7
8
9
1.不确定,因为没有传参啊	
2.window
3.undefined(use strict)
4.obj // . 前面的
5.window/undefined
6.button //浏览器保证的,只要点击了,this === button
7.$('#test') //JQ文档写的 this指向了当前正在执行事件的元素
8.li //JQ文档 this 代表了与 selector 相匹配的元素
9.new 出来的对象 vm //文档说的,因为它call了new 出来的东西

那么这么蛋疼的this为什么还要用它,我们要用箭头函数!!

箭头函数自己没有 thisarguments

它的 this 是外面的 this

1
2
3
4
var f = ()=>{console.log(this)} f() //window 
var obj = { sayThis : ()=>{console.log(this)}} ; obj.sayThis
// window ( 如果是function 那就是obj了)
//因为obj的this是window,所以里面的也是window, ()=>{} 不接收this,也不修改this,外面是什么,它就是什么

1.3 bind是什么

先看下MDN的定义

bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。

做几个实验

//
1
2
3
4
5
6
function fn1(){ console.log(this) } //this是啥?不知道
fn1()// 哦,this 是 window
fn1.call(1) // this又是 Number(1) 了
//我想写一个直接就知道this是什么的函数
function fn2(){ fn1.call(1) } //this 肯定是 Number(1) ,我都钦定了,但是这么写有点啰嗦,JS帮我整个方法
var fn3 = fn1.bind(1) ; fn3() // Number(1)

上面我们说到了,this 其实只是函数的第一个参数而已,第二个参数是arguments,我能不能也钦定一下子?

1
2
3
4
5
6
7
function fn1(){ console.log(this) ;console.log(arguments) } 
fn1() // window []
function fn2(){ fn1.call(1,'a','b','c') }
fn2()// Number(1) ['a','b','c']
//那这样的话,bind是不是也能指定arguments呢?
var fn3 = fn1.bind(1,'a','b','c');
fn3()// Number(1) ['a','b','c']

所以说bind()就是返回了一个函数,这个函数 call 原来的函数,而call的值你可以自己指定

1.4 new 是什么

1.4.1 为什么要有 new ,我们的需求

​ 我们想要 批量创建对象

1.4.2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Solider(name){
this.ID = i
this.name = name || 'no name';
this.life = 42
}
Solider.prototype.兵种 = '解放军';
Solider.prototype.攻击力 = 100;
Solider.prototype.攻击 = function (){ };

var soliders = [];
for(var i = 0 ; i < 100 ; i++){
soliders.push( new Solider() )
}
console.log(soliders)

习俗:

  1. 构造函数首字母大学

    2. 构造函数可以省略掉 create
    3. 如果构造函数没有参数,可以省略括号。
    

面向对象

继承
1
2
3
4
5
6
7
8
//prototype和 __proto__的区别	
//prototype是函数特有的属性
function a(){};
a.prototype
//访问 __proto__需要 a.prototype.__proto__
var a = 1;
a.__proto__ = Number{};
a.prototype = undefined

ES5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function Human(options){
//this = {}
//this.__proto__ = Human.prototype JS之父的关怀。将这个空对象this的公有属性连上了 Human的公有属性
this.name = options.name;
this.肤色 = options.肤色 //特有属性
//return this 都是 JS之父的关怀
}
Human.prototype.eat = function(){}; //公有属性

function Solider(options,id){
Human.call(this,options) //用Solider里的this,放到 Human 里面用,这样Soilder就有了 name 和 say 的特有属性
this.id = id;
this.life = 42;
}
Solider.prototype.attack = function(){};
//Solider.prototype.__proto__ = Human.prototype; 生产环境不能用
//但是 new 可以用,看看上面JS之父的关怀,我们换一种写法
//兼容IE
function fakeHuman(){}
fakeHuman.prototype = Human.prototype;
Solider.prototype = new fakeHuman()
//Solider.prototype === fakeHuman.prototype === Human.prototype;

//不兼容IE
Solider.prototype = Object.create(Human.prototype);

var s = new Solider({name:'小小',肤色:'yellow'},10)

ES6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Human {
constructor(options) {
this.name = options.name
this.肤色 = options.肤色
}
eat() {}
}
class Solider extends Human {
constructor(options, id) {
super(options)
this.id = id,
this.life = 42
}
attack() {}
}
var s = new Solider({name:'小小',肤色:'yellow'},10)

作业题1

要求不使用 class,完成如下需求:

写出一个构造函数 Animal
输入为一个对象,如 {物种:’xxx’}
输出为一个新对象,该对象的共有属性为 {物种:’xxx’,行动: function(){}},没有自有属性
再写出一个构造函数 Human
Human 继承 Animal
输入为一个对象,如 {name: ‘Frank’, birthday: ‘2000-10-10’}
输出为一个新对象,该对象自有的属性有 name 和 birthday,共有的属性有物种、行动和使用工具
在写出一个构造函数 Asian
Asian 继承 Human
输入为一个对象,如 {city: ‘北京’, name: ‘Frank’, birthday: ‘2000-10-10’ }
输出为一个新对象,改对象自有的属性有 name city 和 bitrhday,共有的属性有物种、行动和使用工具和肤色

最后一个新对象是 Asian 构造出来的
Asian 继承 Human,Human 继承 Animal
注意,不要使用 class 关键字,使用课上讲的原型链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
function Animal() {

}
Animal.prototype.行动 = function () {};

function Human(options) {
this.name = options.name;
this.birthday = options.birthday;
}

// Human.prototype = Object.create(Animal.prototype)
function fake(){}
fake.prototype = Animal.prototype;
Human.prototype = new fake();
//关键点 此时Human.prototype.constructor指向function Animal(){}
//因此需要改变constructor指向Human
Human.prototype.constructor = Human
Human.prototype.物种 = '人类';
Human.prototype.使用工具 = function () {};

var s = new Human({
name: 'Frank',
birthday: '2000-10-10'
})

function Asian(options) {
//关键点,将Human的特有属性,放到Asian来
Human.call(this, options)
this.city = options.city;

}
Asian.prototype.肤色 = 'yellow'
//继承的关键点
// Asian.prototype = Object.create(Human.prototype)
function fake2(){}
fake2.prototype = Human.prototype;
Asian.prototype = new fake2();
//修改 constructor 的指向
Asian.prototype.constructor = Asian
Asian.prototype.肤色 = 'yellow'
var h = new Asian({
city: '北京',
name: 'Frank',
birthday: '2000-10-10'
})

作业题2

要求使用 class,完成如下需求:

写出一个构造函数 Animal
输入为一个对象,如 {物种:’xxx’}
输出为一个新对象,该对象的共有属性为 {行动: function(){}},自有属性为 {物种:’xxx’}
再写出一个构造函数 Human
Human 继承 Animal
输入为一个对象,如 {name: ‘Frank’, birthday: ‘2000-10-10’}
输出为一个新对象,该对象自有的属性有 name、物种和 birthday,共有的属性有行动和使用工具
在写出一个构造函数 Asian
Asian 继承 Human
输入为一个对象,如 {city: ‘北京’, name: ‘Frank’, birthday: ‘2000-10-10’ }
输出为一个新对象,改对象自有的属性有 name city 物种 肤色和 bitrhday,共有的属性有行动和使用工具

最后一个新对象是 Asian 构造出来的
Asian 继承 Human,Human 继承 Animal
注意,要使用 class 关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Animal{
constructor(){

}
行动(){}
}

class Human extends Animal{
constructor(options){
super()
this.name = options.name
this.birthday = options.birthday
}
使用工具(){}
}

class Asian extends Human{
constructor(options){
super(options)
this.city = options.city
this.物种 = '人类'
this.肤色 = '黄色'
}
}

var h = new Asian({city: '北京', name: 'Frank', birthday: '2000-10-10' })