继承的目的:让子类的实例同时也具备父类中的私有属性和公共方法
子类的实例能够用子类私有的和原型上共有的
父类的实例能够用父类私有的和原型上共有的
方法一:原型继承
将子类的原型对象指向父类的实例,通过这种方法,子类能继承父类私有的和公有的。
习题1:
function Parent() {
this.name = "Parent";
this.sex = "boy";
}
Parent.prototype.getName = function () {
console.log(this.name);
};
function Child() {
this.name = "child";
}
Child.prototype = new Parent();
var child1 = new Child();
child1.getName();//child
console.log(child1);//Child {name: "child"}
//sex、getName都是Child原型对象上的属性,所以并不会表现在child1上。
复制代码
习题2:
function Parent(name) {
this.name = name;
this.sex = "boy";
this.colors = ["white", "black"];
}
function Child() {
this.feature = ["cute"];
}
var parent = new Parent("parent");
Child.prototype = parent;
var child1 = new Child("child1");
child1.sex = "girl";
child1.colors.push("yellow");
child1.feature.push("sunshine");
var child2 = new Child("child2");
console.log(child1);
console.log(child2);
console.log(child1.name);
console.log(child2.colors);
复制代码
- 优点:
- 继承了父类的模板,又继承了父类的原型对象
- 缺点:
- 如果要给子类的原型上新增属性和方法,就必须放在Child.prototype = new Parent()这样的语句后面(顺序问题)
- 无法实现多继承(因为已经指定了原型对象了)
- 来自原型对象的所有属性都被共享了,这样如果不小心修改了原型对象中的引用类型属性,那么所有子类创建的实例对象都会受到影响(这点从修改child1.colors可以看出来)
- 创建子类时,无法向父类构造函数传参数(这点从child1.name可以看出来)
instanceof检测
instanceof运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
再来看看通俗点的简介:a instanceof B
实例对象a instanceof 构造函数B
检测a的原型链(proto)上是否有B.prototype,有则返回true,否则返回false。
function Parent () {
this.name = 'parent'
}
function Child () {
this.sex = 'boy'
}
Child.prototype = new Parent()
var child1 = new Child()
console.log(child1 instanceof Child)//true
//查找顺序:child1 -> child1.__proto__ -> Child.prototype
console.log(child1 instanceof Parent)//true
//查找顺序:child1 -> child1.__proto__ -> Child.prototype-> Child.prototype.__proto__ -> Parent.prototype
console.log(child1 instanceof Object)//true
复制代码
isPrototypeOf()
这个方法属于Object.prototype上的方法,isPrototypeOf()的用法和instanceof相反。
它是用来判断指定对象object1是否存在于另一个对象object2的原型链中,是则返回true,否则返回false。
function Parent() {
this.name = "parent";
}
function Child() {
this.sex = "boy";
}
Child.prototype = new Parent();
var child1 = new Child();
console.log(Child.prototype.isPrototypeOf(child1));//true
console.log(Parent.prototype.isPrototypeOf(child1));//true
console.log(Object.prototype.isPrototypeOf(child1));//true
复制代码
方法二:call 继承
在子类构造函数中,把父类当做普通方法执行(没有父类实例,父类原型上的那些东西也就和它没关系了),并且用call改变this指向。
Parent.call(this);
CALL 继承(只能继承父类中私有的,让父类私有的变成子类私有的;不能继承父类中公共的)
function Parent() {
this.x = 100;
}
Parent.prototype.getX = function getX() {
return this.x;
};
function Child() {
// 在子类构造函数中,把父类当做普通方法执行(没有父类实例,父类原型上的那些东西也就和它没关系了)
// this -> Child的实例c1
Parent.call(this); // this.x=100: 相当于强制给c1这个实例设置一个私有的属性x,属性值100,相当于让子类的实例继承了父类的私有的属性,并且也变为了子类私有的属性 “拷贝式”
this.y = 200;
}
Child.prototype.getY = function getY() {
return this.y;
};
let c1 = new Child();
console.log(c1);
复制代码
方法三:寄生组合式继承(CALL+另类原型继承)
第一步:让子类原型通过原型链指向父类原型,这样父类公有方法就变成子类公有方法了。
Child.prototype.__proto__=Parent.prototype
兼容写法:Child.prototype=Object.create(Parent.prototype);
这句话的含义是:创建一个空对象指向父类的原型,然后让子类的原型指向空对象第二步 在子类构造函数中,把父类当做普通方法执行,并让this指向子类实例。
Parent.call(this)
第三步让原型的constructor正常指回,指向所属类。
Child.prototype.constructor = Child;
function Parent() {
this.x = 100;
}
Parent.prototype.getX = function getX() {
return this.x;
};
function Child() {
Parent.call(this);
this.y = 200;
}
Child.prototype = Object.create(Parent.prototype);
// 重定向了Child.prototype,重新指向Child
Child.prototype.constructor = Child;
Child.prototype.getY = function getY() {
return this.y;
};
let c1 = new Child();
console.log(c1);
复制代码
方法四:ES6 类与继承
4.1 class类
ES6的类可以包含构造函数方法、实例方法、获取函数、设置函数和静态类方法,但都不是必须的。
实际上 class只是一个语法糖 是构造函数的另一种写法;
class不存在变量提升 (由于继承有关 必须确保子类在父类之后定义)。
class Person{
}
console.log(typeof Person) //funciton
console.log(Person.prototype.constructor === Person) //true
复制代码
class Student {
constructor(name, number) {
this.name = name;
this.number = number;
this.gender = "semale";
}
sayHi() {
console.log(`姓名 ${this.name} ,学号 ${this.number}`);
console.log("姓名:" + this.name, "学号:" + this.number);
}
}
const luozhi = new Student("洛枳", 12345);
console.log(luozhi); //{name: "洛枳", number: 12345, gender: "semale"}
luozhi.sayHi();
/*
姓名 洛枳 ,学号 12345
姓名:洛枳 学号:12345
*/
复制代码
4.1.1 类的属性和方法
定义在constructor 内的属性和方法,即调用在this上,属于
实例属性和方法
否则属于原型属性和方法
.
class Person {
constructor (name) {
this.name = name //constructor内定义的方法和属性是实例对象自己的,
}
say () { //而constructor外定义的方法和属性则是所有实例对象可以共享的 注意!
console.log('hello')
}
}
let jon = new Person()
jon.hasOwnPrototype('name') //true
jon.hasOwnPrototype('say') //false
复制代码
4.1.2 静态方法
不能通过实例对象,可以直接通过类来调用的方法,其中的 this 指向类本身
静态方法可以被子类继承
class Person {
static doSay () {
this.say()
}
static say () {
console.log('hello')
}
}
Person.doSay() //hello
复制代码
4.1.3 取值函数(getter)和存值函数(setter)
class Person {
get name () {
return 'getter'
}
set name(val) {
console.log('setter' + val)
}
}
let jon = new Person()
jon.name = 'jon' // setter jon
jon.name // getter
复制代码
4.2 类的继承
- 在子类后面添加
extends
并指定父类的名称- 在子类的 constructor 构造函数中通过
super
方法借助父类的构造函数,也可以调用父类的普通函数。
class Parent {
constructor() {
this.x = 100;
}
// Parent.prototype.get X = function... ,在Parent.prototype上添加方法getX
getX() {
return this.x;
}
}
// 继承: extends Parent(类似于寄生组合继承)
// 注意:继承后,【如果子类有constructor,】一定要在constructor第一行加上super
class Child extends Parent {
constructor() {
// 类似于我们之前的CALL继承。super(100,200):相当于把Parent中的constructor执行,传递了100和200
super();
this.y = 200;
}
getY() {
return this.y;
}
}
let c1 = new Child();
console.log(c1);
// Child();ES6中创建的就是类,不能当做普通函数执行,只能new执行
复制代码
补充案例
function wjl(name, age) {
// name、age通过参数传进来的,是每一个实例对象私有的属性。
// height、house不是通过参数传进来的,是所有实例对象共有的属性。
this.name = name;
this.age = age;
this.height = 188;
this.house = function () {
return "兰博基尼";
};
}
wjl.prototype.money = 888;
var obj = new wjl("王健林", 12); // obj是wjl的实例对象, 参数 '王健林'、12是obj这个实例对象私有的,不是wjl所有的实例对象共有,这里的 '王健林'、12 和 wsc的实例对象o 没有关系
console.log("obj-------", obj); // wjl {name: "王健林", age: 12, height: 188, house: ƒ}
function wsc(names, ages) {
this.names = names;
this.ages = ages;
wjl.call(this); // 继承wjl,而不是wjl的实例
}
var o = new wsc("王思聪", 22, 33, 55);
console.log("o----", o); // wsc {names: "王思聪", ages: 22, name: undefined, age: undefined, height: 188, …}
// 【'王健林'、12是obj实例的私有属性,不是wjl类的所有实例继承的属性。如果在wjl类中写 this.height = 188,而不是传进来的参数,那么this.height = 188就是wjl类的所有实例继承的属性、属性值,就像wjl类的this.house。】
console.log(o.house, o.name, o.age); // 函数 undefined undefined
console.log(o.house, o.names, o.ages); // 函数 王思聪 22
复制代码
五.补充对象的一些操作
5.1 获取对象类型—-对象是由哪些类创造出来的呢?
5.1.1 constructor-获取的是直接所属的类
let arr = new Array();
console.log(arr.constructor.name); // Array
let p = new Person();
// console.log(typeof p); // object
console.log(p.constructor.name); // Person
复制代码
5.1.2 instanceof-判断 “对象” 是否是指定构造函数的 “实例”
只要 构造函数的原型对象出现在实例对象的原型链中都会返回true
let arr = new Array();
console.log(arr instanceof Array); //true
console.log(arr instanceof Object); //true
class Person {}
let p = new Person();
console.log(p instanceof Person); //true
console.log(p instanceof Array); //false
console.log(p instanceof Object); //true
复制代码
5.1.3 isPrototypeOf—判断 一个对象是否是另一个对象的原型
只要调用者在传入对象的原型链上都会返回true
class Person {
name = "lnj";
}
let p = new Person();
console.log(Person.prototype.isPrototypeOf(p)); // 判断Person原型对象是否是实例对象p的原型,返回true
复制代码
5.2 判断对象属性
5.2.1 in
判断某一个对象类中或原型中是否拥有某一个属性可以用“in”,只要类中或者原型对象中有, 就会返回true
class Person{
name = null;
age = 0;
}
Person.prototype.height = 0;
let p = new Person();
console.log("name" in p); // true
console.log("height" in p); // true
复制代码
5.2.2 hasOwnProperty
判断某一个对象自身是否拥有某一个属性可以用hasOwnProperty
特点: 只会去类中查找有没有, 不会去原型对象中查找
let p = new Person();
console.log(p.hasOwnProperty("name")); // true
console.log(p.hasOwnProperty("height")); // false
复制代码
5.3对象属性增删改查、遍历
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
// 1.创建实例对象
let p = new Person("蔡徐坤", "22");
console.log(p); //{name: "蔡徐坤", age: "22"}
//2.给对象增加属性(增加没有的属性)
p.sex = "男";
p["hobby"] = "singing,dance";
console.log(p); // {name: "蔡徐坤", age: "22", sex: "男", hobby: "singing,dance"}
//3.给对象删除属性(删除已有的属性)
delete p.sex;
console.log(p); // {name: "蔡徐坤", age: "22", hobby: "singing,dance"}
//4.给对象修改属性(对操作已有的属性)
p.name = "蔡小葵";
p["age"] = 23;
console.log(p); // {name: "蔡小葵", age: 23, hobby: "singing,dance"}
// 5.查询属性
console.log("我是" + p.name, "我的爱好是" + p["hobby"]); //我是蔡小葵 我的爱好是singing,dance
// 6.对象属性遍历(for...in...)
for (let key in p) {
console.log(key + ":" + p[key]);
/*
name:蔡小葵
age:23
hobby:singing,dance
*/
}
复制代码