1. 原型
1.1 原型:
每个 函数 都自带一个 prototype属性 ,它是一个 对象 。prototype就是函数的原型。
1.2 原型的作用:
给构造函数添加公共的属性和方法。不建议将属性放在原型上面(因为每个对象都有自己独特的属性值)
1.3 批量添加原型方法
-
可以给一个新对象,但是新对象里面需要手动添加构造器(constructor)
function Person(name,age){ this.name=name; this.age=age; } // 1. 原型上添加一些方法:直接添加 Person.prototype.eat=function(){}; Person.prototype.run=function(){}; // 2. 原型上添加一些方法:新对象+构造器 Person.prototype={ constructor:Person; eat(){}; run(){}; }
1.4 通过原型重写数组的高阶方法
1.4.1 重写forEach ---> 遍历,无返回值
Array.prototype.myForeach = function (callback){
for(let i = 0; i < this.length; i++){
callback(this[i],i,this); // 原型方法里面的this,谁调用this指向谁
}
}
1.4.2 重写map ---> 遍历,返回一个数组
Array.prototype.myMap = function(callback){
let arr = [];
for(let i = 0; i < this.length; i++){
arr.push(callback(this[i],i,this)); // 原型方法里面的this,谁调用this指向谁
}
return arr;
}
1.4.3 重写some ---> 判断是否有成员满足条件,有一个满足就返回true
Array.prototype.mySome = function(callback){
for(let i = 0; i < this.length; i++){
if(callback(this[i],i,this)){ // 原型方法里面的this,谁调用this指向谁
return true;
};
}
return false;
}
1.4.4 重写every ---> 判断是否所有成员满足条件,所有满足才返回true
Array.prototype.myEvery = function(callback){
for(let i = 0; i < this.length; i++){
if(!(callback(this[i],i,this))){ // 原型方法里面的this,谁调用this指向谁
return false;
};
}
return true;
}
1.4.5 重写filter ---> 过滤掉不满足条件的成员,返回符合条件的成员组成的数组
Array.prototype.myFilter = function(callback){
let arr = [];
for(let i = 0; i < this.length; i++){
if(callback(this[i],i,this)){ // 原型方法里面的this,谁调用this指向谁
arr.push(this[i]);
};
}
return arr;
}
1.4.6 重写reduce ---> 累加器,返回最终的累加值
Array.prototype.myReduce = function(callback,init){
let pre = arguments.length == 2 ? init : this[0];
let index = arguments.length == 2 ? 0 : 1;
for(let i = index ; i < this.length; i++){ // 原型方法里面的this,谁调用this指向谁
pre = callback(pre,this[i],i,this);
}
return pre;
}
2. 原型链
2.1 指针:
每个 实例对象 都会自带一个 __proto__属性 ,__proto__就是对象的指针。指向构造函数的原型。
2.2 原型链:
当实例对象访问某个属性或方法时,先会通过实例对象的构造器constructor访问构造函数内部的属性和方法,如果不存在,则会通过 实例对象.__proto__ 访问构造函数的原型对象上的属性和方法,如果还是查找不到,继续通过.__proto__查找上一层的原型,直到null还查找不到就报错,这个查找的过程就叫原型链。
3. 面向对象
3.1 面向对象的方法:
- 面向对象分析 OOA :有哪些对象
- 面向对象设计 OOD :对象和对象之间的关系
- 面向对象编程 OOP :编程阶段
3.2 面向对象编程的3大特点:
- ①
封装:将相同的属性和方法提取成为一个类-
类:抽象。 对象:具体。
-
类是对象的模板,对象是类的实例
-
- ②
继承:-
代码复用:子类会继承父类的属性和方法
-
更灵活:子类可以添加或者修改属性或者方法
-
- ③
多态:-
重写(override):子类重写父类的属性和方法(基于继承)
-
重载(overload):在同一个类中,同名不同参(函数名相同,参数不同)
-
【注意】JS中不存在重载,因为js的函数名相同会被覆盖,但是可以模拟出重载。
class Person{ show(){ if(arguments.length==1){ return arguments[0]*10; } if(arguments.length==2){ return arguments[0]*arguments[1]; } } }
-
4. 纯函数
- 有参数的输入,有结果的输出。
- 不会产生副作用(不会对函数以外的变量产生影响)。
5. 回调函数
5.1 回调函数:
- 把一个函数当作另外一个函数的参数,在另外一个函数内部被执行或传递参数。
5.2 回调函数的好处:
- 解决异步
- 扩展函数的功能
6. 构造函数
构造函数上的属性和方法优先于原型上的属性和方法。
// 创建相同的对象
var obj1 = {name:"小A",age:20,run:function(){}};
var obj2 = {name:"小B",age:18,run:function(){}};
// 解决重复创建相同对象,采用函数工厂,但是对象名称无法区分
function createObj(name,age){
var o = {};
o.name=name;
o.age=age;
o.run=function(){};
return o;
}
var o1 = createObj("小A",20);
var o2 = createObj("小B",18);
// 构造函数,解决重复创建相同对象和对象名称不明确的问题.(构造函数的函数名首字母大写,和普通函数进行区分)
function Person(name,age){
// 1. 在构造函数的内部创建了一个空对象
var obj = new Object();
// 2. 空对象的指针指向构造函数的原型
obj.__proto__=Person.prototype;
// 3. 构造函数的this指向空对象
Person.call(obj,xxx)
// 4. 在空对象上添加属性和方法
this.name=name;
this.age=age;
this.run=function(){};
// 5. 隐式的 return this
return this;
}
var p1 = new Person("小A",20);
var p2 = new Person("小B",18);
【重点】new操作符做了什么事情?
-
- 在构造函数内部创建一个空对象
-
- 空对象的指针__proto__指向构造函数的原型对象prototype
-
- 构造函数的this指向空对象
-
- 在空对象上添加属性和方法
-
- 隐式的return this
7. 继承
7.1 继承:
- 子类继承父类的属性和方法。
7.2 继承的优点:
- 代码复用:子类可以继承父类的属性和方法
- 灵活:子类可以追加或者修改属性和方法
7.3 继承的类型:
7.3.1 原型链继承
- ① 原型链继承:
子类的原型指向父类的实例对象 - ② 原型链继承的缺点:子类实例化的时候无法传递参数
7.3.2 对象冒充继承
- ① 对象冒充继承(又叫构造函数继承):
利用父类调用 call() 或 apply() 或 bind() - ② 对象冒充继承的优点:子类能属性初始化,并且所有的属性不是共享的
- ③ 对象冒充继承的缺点:无法继承父类原型prototype上的属性和方法
7.3.3 组合继承:原型链继承+对象冒充继承
- ① 组合继承:
原型链继承+对象冒充继承 - ② 组合继承的优点:子类能属性初始化,能继承父类原型上的属性和方法
- ③ 组合继承的缺点:原型上会多出一些值为undefined的属性
7.3.4 寄生组合继承:寄生继承+对象冒充继承
- ① 寄生组合继承:
寄生继承+对象冒充继承 - ② 寄生组合继承的优点:子类能属性初始化,能继承父类原型上的属性和方法,子类的原型上没有多余的属性
- ③ 寄生组合继承的缺点:代码比较多
【寄生继承】
function inhert(base,child){
// 1. 创建一个父类原型的副本
var basePrototype = Object.create(base.prototype);
// 2. 给创建的副本设置构造器constructor
basePrototype.constructor = child;
// 3. 将副本赋值给子类的原型
child.prototype = basePrototype;
}