继承

编程语言的佛魔道

流派

面向对象

  • 总所周知的Java 一切皆对象(一不自觉我就想到think in java里的特别形象的灯泡图,真的是秒懂什么是方法什么是类)
  • 什么是类(同一事物的抽象描述)
  • 对象(实例)
  • 封装
  • 继承
  • 多态
  • 反射

函数式

  • JavaScript
  • 算子
  • lamda
  • 柯里化
  • 高阶函数
  • 纯函数

所以这就好比佛教和道教,每个流派都有自己的信仰。所以说编程语言上升到宗教层面还是挺可怕的

如果说上面不好理解,可以说说军队,武警和解放军虽然都是国防力量,但是性质不一样,一个对内(处突维稳,抢险救灾)一个对外(抵抗外敌)

所以说不能拿java特有的东西去看待js

JS之假继承

ES6新特性class出来之前 JS是没有继承的(都是模拟的)

JS有原型「prototype」共有属性

1
2
3
4
5
6
var a = new Object()
// a可以调用toString()
a.toString()

//我们知道 a.__proto__ = Object.prototype
Object.prototype 上有toString()

1
2
3
4
5
6
7
8
9
// Object是所有对象的基类

var a = new Array()
a.valueOf() //a自己没有valueOf()

//Array的原型没有valueOf()
Array.__proto__ = Array.prototype
//Object的原型有valueOf() 二次的原型查找
Array.__proto__.__proto__ = Object.prototype

JS是没有类的概念的,只有原型

但是面试官认为是有的。。。(因为人都会拿相似的东西去推测没接深入触过的东西)

什么是类

能产生对象的东西就是「类」

  1. 非常纯粹的人类对象
1
2
3
function Human(){}
var person = new Human()
// person就是一个对象了
  1. 人类应该有个名字
1
2
3
4
5
function Human(name){
this.name = name
}
var person = new Human('aa')
person.name // aa
  1. 人类应该有些技能比如 跑
1
2
3
4
5
6
7
8
9
function Human(name){
this.name = name
}
Human.prototype.run = function(){
console.log('run')
}
var person = new Human('aa')
person.name // aa
person.run() // run
  1. 人类还有子类 比如 男人
1
2
3
4
5
6
7
8
// 男人类 他有一个属性就是性别
function Man(name){
this.gender = '男'
}
// 男人天生会打架
Man.prototype.fight = function(){
console.log('糊你熊脸')
}
  1. 我们希望 Man 应该有个名字
1
2
3
4
5
6
7
8
9
10
11
12
13
//人类
function Human(name){
this.name = name
}
// 男人类 他有一个属性就是性别
function Man(name){
Human.call(this,name)
this.gender = '男'
}
// 男人天生会打架
Man.prototype.fight = function(){
console.log('糊你熊脸')
}
  1. 我们希望 Man 会跑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//人类
function Human(name){
this.name = name
}
Human.prototype.run = function(){
console.log('run')
}
// 男人类 他有一个属性就是性别
function Man(name){
Human.call(this,name)
this.gender = '男'
}
// 男人天生会打架
Man.prototype.fight = function(){
console.log('糊你熊脸')
}
Man.prototype.__proto__ = Human.prototype;

//然后你声明一个 m1
var m1 = new Man('m1')
m1.__proto__ = Man.prototype
m1.__proto__.__proto__ = Human.prototype
m1.__proto__.__proto__.__proto__ = Object.prototype

以上就是一个ES5完整的继承

核心只有两句话

1
2
Human.call(this,name)
Man.prototype.__proto__ = Human.prototype;

但是IE里不允许你操作「下划线proto下划线」

我们可以像这样来模拟继承

1
Man.prototype = new Human()

虽然 new Human()产生的对象的

但是这样 new Human( )会产生一个对象 假设为temp

temp会自带一个name属性 这个是我们不想要的

我们只想要Human的原型上的东西

于是我们用这三句代替

1
2
3
4
5
6
7
8
//step1声明一个空函数,里面什么属性也没有
var f = function(){}

//step2 让空函数「f」原型 = Human.prototype
f.prototype = Human.prototype

//step3 生成一个「f」对象因为它是个空函数
Man.prototype = new f()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Human(name){
this.name = name
}
Human.prototype.run = function(){
console.log("我叫"+this.name+",我在跑")
return undefined
}
function Man(name){
Human.call(this, name)
this.gender = '男'
}

var f = function(){}
f.prototype = Human.prototype
Man.prototype = new f()

Man.prototype.fight = function(){
console.log('糊你熊脸')
}

ES6诞生了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Human{
constructor(name){
this.name = name
}
run(){
console.log("我叫"+this.name+",我在跑")
return undefined
}
}
class Man extends Human{
constructor(name){
super(name)
this.gender = '男'
}
fight(){
console.log('糊你熊脸')
}
}

extends就是为了连接原型链

1
2
Man extends Human 就是为了实现
Man.prototype.__proto__ = Human.prototype
1
super(name) 就是Human.call(this,name)

题外话

ES5方式的继承印证了,我对JS内存的理解

ES6方式你可以自行去浏览器里打印,你会发现它还是用的prototype实现继承的

也就是说ES6的继承会翻译成ES5的写法,如果你是Js程序员应该用ES5的写法,ES6是方便java程序员的写法。

如果你想给 Human 的原型添加一个种族是 人类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ES5
Human.prototype.s = '人类'


// ES6 对不起,不支持,有一种变通的写法

class Human{
constructor(name){
this.name = name
}
//麻烦不????
//麻烦不????
//麻烦不????
get s(){
return '人类'
}

run(){
console.log("我叫"+this.name+",我在跑")
return undefined
}
}

ES5写法证明你是JS程序员

ES6写法证明你是Java程序员