1.对象冒充
构造函数使用this关键字给所有属性和方法赋值(即采用类声明的构造函数方式)。因为构造函数只是一个函数,所以可以使parent这个函数成为children的方法,然后调用它。children就会收到parent的构造函数中定义的属性和方法
var Parent = function (name) {
this.name = name;
this.sayHi = function () {
console.log("hi!"+this.name+".");
}
}
var Children = function (name) {
this.method = Parent;//method类似于property,只是这个method指向的必须是方法
this.method(name);
delete this.method;
this.getName = function () {
console.log(this.name);
}
}
var p = new Parent("john");
var c = new Children("joe");
p.sayHi();
c.sayHi();
c.getName();
在上面的代码中,在children函数里面调用了parent,将parent赋给了this.method,也就是说此时的this.method指向了parent,下一行代码就执行了this.method,相当于将name传给了parent然后执行了一次,此时children就拥有了parent的属性和方法了。
而为什么不直接执行parent呢?而是在里面执行呢?因为在函数内this是指向window(因为函数执行时,实际是window调用了它,也就是window.函数名();那么,里面的this指向当前调用该函数的对象,就是window。)。当把parent赋值给children的method的时候,this就指向了children的实例了。
2.原型链继承
var Parent = function () {
this.name = "john";
this.sayHi = function () {
console.log("hi!"+this.name+".");
}
};
var Children = function () {
};
Children.prototype = new Parent();
var p = new Parent();
var c = new Children();
p.sayHi();
c.sayHi();
在JavaScript中,某个对象中的prototype中所有的属性和方法都会被赋予它对应的实例。
调用 Parent 的构造函数,没有给它传递参数。这在原型链中是标准做法。要确保构造函数没有任何参数。
3.使用call或者apply
这两个函数都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。因为this总是指向调用某个方法的对象,但是使用call和apply的时候,就会改变this的指向。
window.color = 'red';
document.color = 'yellow';
var s1 = {color: 'blue' };
function changeColor(){
console.log(this.color);
}
changeColor.call(); //red (默认传递参数)
changeColor.call(window); //red
changeColor.call(document); //yellow
changeColor.call(this); //red
changeColor.call(s1); //blue
var Pet = {
words : '...',
speak : function (say) {
console.log(say + ''+ this.words)
}
}
Pet.speak('Speak'); // 结果:Speak...
var Dog = {
words:'Wang'
}
//将this的指向改变成了Dog
Pet.speak.call(Dog, 'Speak'); //结果: SpeakWang
apply的用法:
window.number = 'one';
document.number = 'two';
var s1 = {number: 'three' };
function changeColor(){
console.log(this.number);
}
changeColor.apply(); //one (默认传参)
changeColor.apply(window); //one
changeColor.apply(document); //two
changeColor.apply(this); //one
changeColor.apply(s1); //three
不同点:
-
apply()方法 接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。
-
call()方法 第一个参数和apply()方法的一样,但是传递给函数的参数必须列举出来。
4.混合方式
对象冒充方法的主要问题是必须使用构造函数方式。但是使用原型链,就无法使用带有参数的构造函数了。那么如何选择最好的继承方式呢,那就是两者都用。在JavaScript中创建类的最好方式就是用构造函数定义属性,用原型定义方法。
var Parent = function (name) {
this.name = name;
};
Parent.prototype.sayHi = function () {
console.log("hi!"+this.name+".");
};
var Children = function (name,age) {
Parent.call(this,name);//实现继承的关键
this.age = age;
};
Children.prototype = new Parent();//实现继承的关键
Children.prototype.getAge= function () {
console.log(this.age);
};
var p = new Parent("john");
var c = new Children("joe",22);
p.sayHi();
c.sayHi();
c.getAge();
在上面的代码中,父类和子类中都是在函数体内些属性,将方法些在了prototype属性中,也就是用构造函数定义属性,用原型定义方法。
此外,子类CHildren在构造函数中使用call来继承父类Parent的属性,在原型中继承父类的原型。
5.使用Object.create()方法
var Parent = function (name) {
this.name = name;
};
Parent.prototype.sayHi = function () {
console.log("hi!"+this.name+".");
};
var Children = function (name,age) {
Parent.call(this,name);//实现继承的关键
this.age = age;
};
Children.prototype = Object.create(Parent.prototype);//实现继承的关键
Children.prototype.constructor = Children;
Children.prototype.getAge= function () {
console.log(this.age);
};
var p = new Parent("john");
var c = new Children("joe",22);
p.sayHi();
c.sayHi();
c.getAge();
@ 当执行 Children.prototype = Object.create(Parent.prototype) 这个语句后,Children 的 constructor 就被改变为 Parent ,因此需要将 Children.prototype.constructor 重 新指定为 Children 自身。
6.extends关键字实现
class Parent{
constructor(name,age){
this.name = name;
this.age = age;
}
sayHi(){
console.log("hi "+this.name);
}
}
class Children extends Parent{
constructor(name,age,job){
super(name,age);
this.job = job;
}
getJob(){
console.log(this.job);
}
}
let p = new Parent("john",23);
let c = new Children("joe",22,"teacher");
p.sayHi();
c.sayHi();
c.getJob();