两大编程思想
面向过程(Process-oriented Programming,POP):
根据解决问题的步骤,用函数一步步地实现这些步骤,再依次调用函数。
面向对象编程(Object-oriented Programming,OOP):
将问题分解成一个个功能对象,有对象之间合作来解决问题;
具有 封装性、继承性、多态性 的特性,更加灵活,易复用,易维护。
面向对象的思维特点:
- 抽取对象共有的属性和行为封装成一个类(模板);
- 对这个类进行实例化,创建类的具体对象;
- 使用对象的属性和方法,实现功能。
类是对象的模板,对象是类的实例。
一、ES6 中的类和对象
对象及其属性、方法的定义 见前文 JS(五)。
ES6 全称 ECMAScript 6.0,在 JS 中引入类的概念。
创建类
第一步,创建类 class:
class 类名 {
constructor(形参1, ...) {
//定义共有属性
this.uname = 形参1;
}
//定义共有方法
sing() { ...
}
}
第二步,类的实例化,创建对象:
var 对象名 = new 类名(实参1, ...);
其中使用 构造函数 constructor() 方法用于传递参数,返回到对象实例;
通过 new 生成对象实例时,会自动调用 constructor (不用function)。
注意点
ES6 中没有变量提升,所以必须要先定义类,才能通过类实例化对象;(链式?)
类里面共有的属性和方法一定要加 this.属性/方法;
this 指向问题:在一类class 中,constructor 里面的 this 指向的是实例对象,方法里面的 this 指向的是这个方法的调用者。
类的继承
子类继承父类的属性和方法:
class Father {
constructor() {
}
}
//创建继承父类的子类
class Son extends Father {
constructor() {
//调用父类的构造函数
super(..,..);
}
}
子类继承了父类后,如果子类和父类有相同的属性或方法,子类会执行自己本身的(就近原则);子类中没有就去调用父类中的。
子类可以扩展自己的方法。
super 关键字
在子类中使用,可以调用父类的构造函数,把实参数据传给父类: super (形参1, 形参2,...);
也可以调用父类中的普通函数: super.方法();
如果在子类中要使用 super,也要用 this 设置自己的参数,就要把 super 写在 this 前面。
二、构造函数和原型
创建自定义对象的三种方式:
- 对象字面量
- new Object()
- 自定义构造函数
概述
在 ES6 之前,对象不是基于类创建的,是用构造函数创建的。
构造函数主要用来初始化对象,即为对象成员变量赋初值,常与 new 搭配使用;
new 执行是完成的任务:
- 在内存中创建一个空对象; (会浪费内存)
- 让 this 指向这个空对象;
- 执行构造函数中的代码,给该空对象添加属性和方法;
- 直接返回这个新对象(构造函数里面就不需要return)。
//构造函数
function Star(uname, age,..) {
this.name = uname;
this.age = age;
this.sing = function() {
...
}
}
//创建对象实例
var ldh = new Star('刘德华', 50);
构造函数中的属性和方法称为成员;
分为两类,通过两种方式添加的成员:
静态成员 —— 在构造函数本身上添加的成员,只能由构造对象本身访问;
动态(实例)成员 —— 在构造函数内部创建的对象成员(this.),只能由实例化的对象访问;
原型
构造函数原型 prototype
每一个构造函数都有一个 prototype 属性(也是一个对象);
构造函数可以通过原型 prototype 定义一些方法(函数),被它所有的对象实例所共享。 (共享方法,避免浪费内存)
对象原型 __proto__
每个对象都会有一个 __proto__ 属性,指向构造函数的 prototype 原型对象;(这就是对象能够共享方法的原因)
实例身上的对象原型 __proto__ 与 构造函数身上的原型 prototype 是等价的;
__proto__ 是属性非标准,不能直接使用,它只是为对象查找机制提供一条路线。
(构造函数的原型对象 prototype 也有 __proto__ 属性)
构造函数 constructor
构造函数原型对象(prototype)和对象原型(_proto_)里面都有一个 constructor 属性,称为构造函数;
主要用于记录对象引用于哪个构造函数,让原型对象重新指向最初的构造函数本身;
原型对象 prototype 的函数里面的 this 指向对象实例(调用者);
在给构造函数原型对象 prototype 添加共享方法时,以对象形式赋值的操作会覆盖掉原来默认存在的 constructor 属性,所以要手动添加 constructor 构造函数:
Star.prototype={
//手动添加 constructor
constructor:Star,
sing: function(){
...
},
...
}
内在关系
构造函数(function)、原型对象 prototype、对象实例之间的关系:
原型链
JS 的成员查找机制(按原型链)
- 访问一个对象的属性或方法时,先查找这个对象自身有没有;
- 如果自身没有,就查找对象的原型,即 _proto_ 指向的 prototype 原型对象;
- 如果原型中也没有,就查找原型对象的原型,即 Object 原型对象;
- 依次向上直到 null 为止。
扩展内置对象
通过在原型对象中自定义方法,对内置对象(数组Array、字符串String...)进行方法的扩展。
//给数组扩展一个求和的方法
Array.prototype.sum = function() {
var sum = 0;
for (var i = 0; i < this.length; i++) {
sum += this[i];
}
return sum;
}
继承
ES6 是通过 extends 实现继承,而 ES6 之前是通过 构造函数+原型对象 模拟实现继承,称为组合继承。
call() 方法,可以调用函数,改变 this 的指向:
方法名fn.call ( thisArg, arg1, arg2,... )
其中:thisArg 表示让当前函数fn的 this 指向谁(谁成为调用者);
arg1、2...表示穿传入的实参。
继承属性和方法
function Father(形参) {
...
}
function Son(形参) {
//利用 call() 将 father 的属性继承过来
Father.call(this, 形参...);
}
//利用 new 新建一个实例来继承 father 的方法
Son.prototype = new Father();
//更改了 Son 的原型对象后,还要手动设置好 constructor 属性(指回本身的构造函数)
Son.prototype.constructor = Son;
不能用 Son.prototype = Father.prototype; 直接赋值来实现继承;
—— 因为对象属于复杂数据类型,赋值操作是将地址赋值过去,若继续修改Son.prototype,Father.prototype 也一起被改变。
ES5 新增方法
数组方法
迭代(遍历)方法:
arr.forEach ( function( value, index, array ) {...})
函数里面的参数:
value —— 逐个传递数组中的元素;
index —— 逐个传递数组中元素的索引;
array —— 传递整个数组(可不写)。
会自动循环执行,遍历整个数组。
arr.filter ( function( currentValue, index, array ) {...})
currentValue —— 传递数组当前元素的值;
用于筛选数组,选出符合条件的数组元素,然后返回(return)这些元素组成的一个新数组。
arr.some ( function( currentValue, index, array ) {...})
用于查找数组中是否有符合条件的数组元素,返回 ture 或者 false;
循环查找时,如果找到了第一个符合的元素,就停止循环,返回结果。
字符串方法
str.trim()
删除字符串两端的空白字符。
对象方法
Object.keys(obj)
获取对象自身所有的属性,效果类似于 for ( k in obj );
返回的是一个数组。
Object.defineProperty(obj, prop, descriptor)
直接在对象 obj 上定义一个新属性 prop,或者修改对象的现有属性,并返回此对象。
都是必需参数:
obj —— 目标对象;
prop —— 要定义或修改的属性名;
descriptor —— 要执行的定义或修改属性操作,以及相关设置,都以对象{ } 形式书写:
Object.defineProperty(obj, 'name', {
value: 'ldh', //定义新增属性的值,或者修改已有属性的值
writable: false, //默认false,不允许修改此对象属性(name)
enumerable: false, //默认false,不允许遍历此对象属性
configurable: false //默认false,不允许删除此对象属性,并且不允许再次修改此对象属性的configurable
})