简介:很多概念不清或忘记,重新构建自己的知识体系。每天问自己1~多个问题。我是菜鸟 成为大神之路!
1. JavaScript创建对象的方法?
①语法结构创建的对象使用{},[]
var o = {name: "Lis",sex:16}; | o 这个对象继承了Object.prototype上面的所有属性
|
var arr = ["yo", "whadup", "?"]; | 数组都继承于 Array.prototype (Array.prototype 中包含 indexOf, forEach等方法)
|
function fun(){ | 函数都继承于Function.prototype (Function.prototype 中包含 call, bind等方法)
return 2; |
} |
②构造器创建的对象使用new
在 JavaScript 中,构造器其实就是一个普通的函数。当使用 new 操作符 来作用这个函数时,它就可以被称为构造方法(构造函数)
function Graph() {
this.vertices = [];
this.edges = [];
}
Graph.prototype = {
addVertex: function(v){
this.vertices.push(v);
}
};
var obj = new Graph();
obj是生成的对象,他的自身属性有'vertices'和'edges'.
在obj被实例化时,obj.[[Prototype]]指向了Graph.prototype.
③Object.create 创建的对象>>link
ECMAScript 5
中引入了一个新方法。新对象的原型就是调用 create
方法时传入的第一个参数。
④class 关键字创建的对象
ECMAScript6
引入了一套新的关键字用来实现 class
。使用基于类语言的开发人员会对这些结构感到熟悉,但它们是不同的。JavaScript
仍然基于原型。这些新的关键字包括 class, constructor,static,extends 和 super
。
"use strict";
class Polygon {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
class Square extends Polygon {
constructor(sideLength) {
super(sideLength, sideLength);
}
get area() {
return this.height * this.width;
}
set sideLength(newLength) {
this.height = newLength;
this.width = newLength;
}
}
var square = new Square(2);
2. JavaScript继承的实现方法?
// 定义一个交通工具类
function Vehicle() {
// 属性
this.name;
this.number;
// 实例方法
this.funBase = function(){
console.log("交通工具名称:"+this.name+"||"+"可乘坐人数:"+this.number);
}
}
// 原型方法
Vehicle.prototype.funRatio = function(ratio) {
console.log(this.name + ':' + ratio);
};
①原型链继承★★
核心: 将父类的实例作为子类的原型
function Car(){
this.getName = function(){
console.log(this.name);
return this.name;
}
};
function Airplane(){
this.getName = function(){
console.log(this.name);
return this.name;
}
};
Car.prototype = new Vehicle();
Car.prototype.name = '汽车';
Car.prototype.number = '20人';
Airplane.prototype = new Vehicle();
Airplane.prototype.name = '飞机';
Airplane.prototype.number = '100人';
var car = new Car();
var airplane = new Airplane();
//测试
car.funBase();
car.funRatio("60%");
car.getName();
airplane.funBase();
airplane.funRatio("36%");
airplane.getName();
console.log(car instanceof Vehicle); // true
console.log(car instanceof Car); //true
执行结果:
特点:
* 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
* 父类新增原型方法/原型属性,子类都能访问到
* 简单,易于实现
缺点:
* 要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中
* 无法实现多继承
* 来自原型对象的所有属性被所有实例共享(来自原型对象的引用属性是所有实例共享的)(详细请看附录代码: 示例1)
* 创建子类实例时,无法向父类构造函数传参
'推荐指数:★★(3、4两大致命缺陷)'
②构造继承★★
核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
function Car(){
Vehicle.call(this);
this.name = '汽车';
this.number = '20人';
this.getName = function(){
console.log(this.name);
return this.name;
}
}
// 测试
var car = new Car();
car.funBase();
car.funRatio("60%");//TypeError: car.funRatio is not a function[详细了解]
car.getName();
console.log(car instanceof Vehicle); // false
console.log(car instanceof Car); //true
执行结果
特点:
* 解决了1中,子类实例共享父类引用属性的问题
* 创建子类实例时,可以向父类传递参数
* 可以实现多继承(call多个父类对象)
缺点:
* 实例并不是父类的实例,只是子类的实例
* 只能继承父类的实例属性和方法,不能继承原型属性/方法
* 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
'推荐指数:★★(缺点3)'
③实例继承★★
核心:为父类实例添加新特性,作为子类实例返回
function Car(){
var vehicle = new Vehicle();
vehicle.name = '汽车';
vehicle.number = '20人';
vehicle.getName = function(){
console.log(vehicle.name);
return vehicle.name;
}
return vehicle;
}
// 测试
var car = new Car();
car.funBase();
car.funRatio("60%");//TypeError: car.funRatio is not a function[详细了解]
car.getName();
console.log(car instanceof Vehicle); // true
console.log(car instanceof Car); //false
执行结果:
特点:
* 不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果
缺点:
* 实例是父类的实例,不是子类的实例
* 不支持多继承
'推荐指数:★★'
④拷贝继承★
核心:逐一复制父类实例的属性方法给子类实例
function Car(){
var vehicle = new Vehicle();
for(var p in vehicle){
Car.prototype[p] = vehicle[p];
}
this.getName = function(){
console.log(this.name);
return this.name;
}
}
// 测试
Car.prototype.name = '汽车';
Car.prototype.number = '20人';
var car = new Car();
car.funBase();
car.funRatio("60%");
car.getName();
console.log(car instanceof Vehicle); //false
console.log(car instanceof Car); //true
执行结果:
特点:
* 支持多继承
缺点:
* 效率较低,内存占用高(因为要拷贝父类的属性)
* 无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)
'推荐指数:★(缺点1)'
⑤组合继承★★★★
核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
function Car(){
Vehicle.call(this);
this.name = '汽车';
this.number = '20人';
this.getName = function(){
console.log(this.name);
return this.name;
}
}
Car.prototype = new Vehicle();
Car.prototype.constructor = Car;//组合继承也是需要修复构造函数指向
// 测试
var car = new Car();
car.funBase();
car.funRatio("60%");
car.getName();
console.log(car instanceof Vehicle); // true
console.log(car instanceof Car); //true
执行结果:
特点:
* 弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
* 既是子类的实例,也是父类的实例
* 不存在引用属性共享问题
* 可传参
* 函数可复用
缺点:
* 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
'推荐指数:★★★★(仅仅多消耗了一点内存)'
⑥寄生组合继承★★★★
核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
function Car(){
Vehicle.call(this);
this.name = '汽车';
this.number = '20人';
this.getName = function(){
console.log(this.name);
return this.name;
}
}
(function(){
// 创建一个没有实例方法的类
var Super = function(){};
//设置原型
Super.prototype = Vehicle.prototype;
//将实例作为子类的原型
Car.prototype = new Super();
// 需要修复下构造函数
Car.prototype.constructor = Car;
})();
// 测试
var car = new Car();
car.funBase();
car.funRatio("60%");
car.getName();
console.log(car instanceof Vehicle); // true
console.log(car instanceof Car); //true
执行结果:
特点:
* 堪称完美
缺点:
* 实现较为复杂
'推荐指数:★★★★(实现复杂,扣掉一颗星)'
3. Function.prototype.call() 实现继承时的原理?(相关apply()、bind()方法的使用)
①call()、apply()、bind() 都是用来重定义 this 这个对象的!
②参数:
* call的参数是直接放进去的,第二第三第n个参数全都用逗号分隔,直接放到后面 obj.myFun.call(db,'成都', ... ,'string' );
* apply的所有参数都必须放在一个数组里面传进去 obj.myFun.apply(db,['成都', ..., 'string' ]);
*
③方法:
* apply:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.apply(A, arguments);即A对象'应用'B对象的方法。
* call:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2);即A对象'调用'B对象的方法。
参考文章:
① www.cnblogs.com/humin/p/455…
② www.cnblogs.com/lengyuehuah…