第一章 灵活语言–JavaScript
1.1 全局变量 命名函数
function fnName() {}
1.2 全局变量 函数表达式
var fnName = function() {}
ps:全局变量污染全局作用域,容易被覆盖
1.3 对象收编变量
1 | var fnObj = { |
1.4 对象另一种形式
JS万事万物皆对象,函数也是对象
首先声明一个对象, 然后添加方法1
2var fnObj = function() {};
fnObj.fn1 = function() {};
ps: 对象不能复制, new
关键字 创建新对象,新对象不能继承方法
1.5真假对象
简单复制,将方法放在一个函数对象中1
2
3
4
5
6
7
8
9var fnObj = function() {
return {
fn1: function() {},
fn2: () => {}
}
}
var a = fnObj();
a.fn1();
ps: 每次调用返回新对象, 状态不会互相干扰 / 新创建对象 和 fnObj
没有任何关系
1.6 类
1.5假对象
新创建对象 和fnObj
没有任何关系1
2
3
4
5
6
7
8var fnObj = function() {
this.fn1 = function(){}
this.fn2 = () => {}
this.fn3 = arguments => {}
}
var a = new fnObj();
a.fn1();
用类创建对象
1.7 检测类
所有方法放在 函数内部 通过this
定义。\
每次通过new
关键字创建新对象,新对象
对类
的this
上的属性/方法
进行复制。新创建对象 都有自己的一套方法,但是造成的消耗很奢侈。
第一种方法1
2
3var fnObj = function() {};
fnObj.prototype.fn1 = function() {};
fnObj.prototype.fn2 = () => {};
实例 共享prototype
上属性/方法
第二种方法1
2
3
4
5
6var fnObj = function() {};
fnObj.prototype = {
fn1: function() {},
fn2: () => {}
}
两种方法不能混用,第二种覆盖第一种.
1.8 方法的链式调用
在声明的 每个方法末尾 将当前对象返回,JS中this
关键字指向当前对象1
2
3
4
5
6
7
8
9
10
11
12
13var fnObj = {
fn1: function() {
// DoSomeThing
return this;
},
fn2: () => {
// DoSomeThing
return this;
}
}
// 链式调用
> fnObj.fn1().fn2();
构造函数原型链式调用1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16var fnObj = function() {};
fnObj.prototype = {
fn1: function() {
// DoSomeThing
return this;
},
fn2: () => {
// DoSomeThing
return this;
}
}
// 使用时需要先创建一下
var a = new fnObj();
a.fn1().fn2();
1.9 函数祖先
prototype.js / lodash.js
, js框架
;
在内置对象Function
原型上添加方法
Function.prototype.fn1 = fucntion() {}
// 函数形式
var f = function() {};
f.fn1();
// 构造函数
var f = new Function();
f.fn1();
此种方法不被推荐,污染原生对象Function
, 可抽象出一个 统一添加方法的 功能方法
1 | // 在原型上挂载`addMethod`方法 |
1.10链式添加
1 | // 在原型上挂载`addMethod`方法 |
1.11 类式调用
1 | // 在原型上挂载`addMethod`方法 |
总结:
JS是一种灵活
的语言, 函数
扮演一等公民。使用JS可以编写出更多优雅的艺术代码
。
忆之获: 函数 多样化 创建/使用. 灵活性
是语言特有气质。团队开发慎重,尽量保证团队代码风格一致。易开发、可维护、代码规范必然要求。
问答:
问: 1.5真假对象 一节中如何实现方法链式调用?\
答:1
2
3
4
5
6
7
8
9
10
11
12
13var fnObj = function() {
return {
fn1: function() {
return this;
},
fn2: () => {
return this;
}
}
}
var fn = fnObj();
fn.fn1().fn2();问:试定义一个可为函数添加多方法的
addMethod
方法。\
答:参考1.10链式添加方式一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 在原型上挂载`addMethod`方法
Function.prototype.addMethod = function(name, fn) {
this[name] = fn;
return this;
}
// 顺便支持`链式调用`
var fn = function() {};
fn.addMethod('fn1', function() {
return this;
}).addMethod('fn2', function() {
return this;
})
fn.fn1().fn2();方式二
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// 定义 参数类型 接口
interface opt = {
fnName: string,
fn: function
}
// 支持 一次性 创建多个方法
Function.prototype.addMethod = function(options: opt[]) {
for(obj of options) {
this[obj.fnName] = obj.fn;
}
trturn this;
}
var methods = [
{
fnName: 'fn1',
fn: function() {}
},
{
fnName: 'fn2',
fn: () => {}
}
...
]
var '自定义Fn' = function() {};
'自定义Fn'.addMethod(methods);问:试定义一个既可为函数添加多方法,又可为原型添加方法的
addMethod
方法。\
答:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 定义 参数类型 接口
interface opt = {
fnName: string,
fn: function
}
// 支持 一次性 创建多个方法
Function.prototype.addMethod = function(options: opt[], proto: string) {
if(proto === "prototype") {
for(obj of options) {
this.protype[obj.fnName] = obj.fn;
}
} else {
for(obj of options) {
this[obj.fnName] = obj.fn;
}
}
trturn this;
}
第二章 面向对象编程
2.1 面向对象
面向过程
面向对象编程 就是 将 需求 抽象成一个对象, 针对对象分析 特征(属性) / 动作(方法). 这个对象称为类
.
核心思想:封装
继承
多继承
多态
2.2 包装明星–封装
2.2.1 创建一个类
首先声明一个函数,保存在一个变量里. 按编程习惯,将类名首字母大写.
然后在函数(类)内部通过对this
(函数内部自带变量,用于指向当前对象)变量添加 属性/方法,实现对类添加属性/方法
1
2
3
4
5var Book = function(id, bookName, price) {
this.id = id;
this.bookName = bookName;
this.price = price;
}
也可通过类
的原型(类本身也是对象,所以也有原型prototype
)上添加属性/方法
.\
两种方式:
①为原型对象属性赋值1
Book.prototype.show = function() {};
②将一个对象赋值给类的原型对象.1
2
3
4Book.prototype = {
show: function() {},
fn2: function() {},
}
两种模式不能混用
使用时需要new
关键字,实例化(创建)新对象.
关于对实例方法和原型方法解释:
通过this
添加的方法/属性
,是在当前对象上添加的\
JS是一种基于prototype
原型的语言, 每创建一个对象(函数也是一种对象),都有一个prototype原型用于指向 其继承的 属性/方法. 通过prototype继承方法非对象自身所有,需要通过prototype逐级查找。
通过this
定义的方法/属性
,是对象自身拥有, 每次通过类
创建新对象实例时,都会复制类
本身方法、属性\
prototype上方法/属性是继承来,状态共享, 不会被多次创建。
2.2.2 属性与方法封装
理解:
通过JS函数作用域 实现 函数内部创建
面向对象思想:属性/方法
的 隐藏、暴露,私有属性/私有方法、公有属性/公有方法、保护属性/保护方法
JS通过函数作用域
实现私有属性/私有方法
;
this
创建公有属性/公有方法
, 通过this
创建的公有方法,可以访问类(创建时)||对象
的私有属性/私有方法
, 由于这些方法权利比较大, 又称为特权方法
;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// 私有属性/私有方法, 特权方法, 对象公有属性/对象公有方法, 构造器
var Book = fucntion(id, name, price) {
// 私有属性
var num = 1;
// 私有方法
function checkId() {};
// 特权方法
this.getName = function() {};
this.getPrice = function() {};
this.setName = function() {};
this.setPrice = function() {};
// 对象公有属性
this.id = id;
// 对象公有方法
this.copy = function() {};
// 构造器
this.setName(name);
this.setPrice(price);
}
理解:
- 通过JS
函数作用域
特征,来实现 函数内部创建 外部无法访问的私有变量/私有方法
- 通过
new
关键字,实例化对象时,会对类
执行一次,所以类
内部this
上定义的属性/方法
自然可复制到 新创建对象上,成为对象公有属性/公有方法
- 其中一些方法能访问到
类
的私有属性/私有方法
, 比外界权利大,得名特权方法
- 通过
new
关键字 实例化对象 时,执行了一遍类
的函数,里面通过调用特权方法
,初始化对象的一些属性。 类
外部通过 点语法 定义 属性/方法: 通过new
关键字创建新对象时,由于类
外面通过 点语法 定义的属性/方法
没有执行到,所以新创建对象中无法获取
,但可通过类
来使用,因此在类外面通过 点语法 定义的 属性/方法 被称为类的静态公有属性/静态公有方法
类
通过prototype
创建的属性/方法
在类实例对象
中可通过this访问(新创建对象proto指向类的原型所指向的对象), 所以将prototype
对象中属性/方法
称为公有属性/公有方法
1 | // 类静态公有属性(实例对象不能访问) |
通过new
关键字创建的对象 实质 是对 新对象this
的不断赋值,并将prototype指向 类的prototype 所指向的对象。
类的构造函数 外,通过点语法定义的属性/方法 是不会添加到 新创建 对象上去的。
想要在新创建对象中使用静态公有属性,得通过 类本身,不能通过this。
类的原型上定义的属性在 新对象里 可直接使用,因为 新对象 prototype和 类 的 prototype 指向同一对象
类私有属性 和 静态方法 在 实例对象中访问不到
公有属性在实例对象中可通过 点语法 访问到
2.2.3 闭包实现
类的静态变量 通过闭包实现
2.2.4 创建对象的安全模式
1 | // 创建一个图书类 |
总结:
- new关键字作用 可看做 对当前对象 this 不停赋值,例中 没有用new, 直接执行函数在全局作用域,this指向 全局变量window;
- 变量book 最终作用 得到Book类(函数)执行结果, 函数没有return语句, 变量book 得不到 Book类 的返回值, 遂为 undefined
安全模式 || 检察长模式1
2
3
4
5
6
7
8
9
10
11var Book = function(title, time, type) {
// 判断执行过程中 this 是否为 当前对象(为true说明是用 new关键字 创建)
if(this instanceof Book){
this.title = title;
this.time = time;
this.type = type;
} else {
// 否则重新创建对象并返回
return new Book(title, time, type);
}
}
总结:
每个类有 3部分
- 第一部分是构造函数内,供实例化对象复制
- 第二部分是构造函数外,直接通过点语法添加,供类直接使用,实例化对象访问不到
- 第三部分是类的原型中,实例化对象可通过原型链间接访问,为所有实例化对象共用
2.3 继承
2.3.1 子类原型对象–类式继承
类式继承1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// 声明父类
function SuperClass() {
this.superValue = true;
};
// 为父类添加公有方法
SuperClass.prototype.getSuperValue = function() {
return this.superValue;
};
// 声明子类
function SubClass() {
this.subValue = false;
}
// 继承父类
SubClass.prototype = new SuperClass();
// 先继承,再为子类添加公有方法
SubClass.prototype.getSubValue = function() {
return this.subValue;
}
// 以上 类似`封装`的过程
总结:
类式继承 需要将 第一个类的实例 赋值给 第二个 类的原型\
类的原型对象 作用是 为类的原型添加公有方法,类不能直接访问原型中的属性/方法
,必须通过 原型prototype 来访问。\
实例化一个父类时,新创建对象复制了父类构造函数内 属性/方法, 并且将 原型proto 指向父类原型对象, 如此便拥有 父类 原型对象/构造函数 上 属性/方法,
instanceof判断对象
与类
继承关系\instanceof
通过判断对象prototype链
来确定对象
是否为某类
实例,不关心对象
与类
自身结构
console.log(obj instanceof Class); // true
console.log(SubClass instanceof ParentClass); // false
instanceof 判断 前面对象是否为 后面 类(对象)实例,不表示两者的继承\
子类 继承 父类 时通过将 父类实例 赋值给 子类原型prototype, 子类原型 继承 父类
console.log(SubClass.prototype instanceof ParentClass); // true
所有对象
都继承自Object
原生对象构造函数
console.log(allObj instanceof Object); // true
类式继承缺点
子类
通过原型prototype
对父类实例化
, 继承父类。