通过【某种方式】让一个对象可以访问到另一个对象中的属性和方法,我们把这种方式称之为继承 并不是所谓的 xxx extends yyy
先来看一个例子
function Person(name,age) {
this.name = name;
this.age = age;
this.say = function(){
console.log(`我叫${
this.name},今年${
this.age}岁`)
}
}
let p1 = new Person("张三",18);
let p2 = new Person("李四",18);
// p1对象和p2对象的say方法是否是同一个方法:false
console.log(p1.say === p2.say);
上面方法中,p1、p2调用say方法时,结果显示并不是同一个方法,也就是没有指向同一块内存,会造成内存浪费
解决方案就是把say方法写在Person.prototype中,那么say方法就是同一个方法了,如下面这个例子
// 为Person增加run方法
Person.prototype.run = function(){
console.log("跑步");
}
//此时p1、p2都能访问到run方法
p1.run();
p2.run();
//验证p1.run和p2.run是否是同一个方法? true
console.log(p1.run === p2.run); // 指向同一个方法,这种方式避免了内存的浪费
console.log(p1.run === Person.prototype.run); // true
// 结论:只要往某个构造函数的prototype对象中添加某个属性、方法,那么这样的属性、方法都可以被所有的构造函数的实例所共享
// --> 这里的【构造函数的prototype对象】称之为原型对象
// Person.prototype 是 p1 p2的原型对象
// Person.prototype是Person构造函数的【实例】的原型对象
// 还是以Person为例
function Person(name,age) {
this.name = name;
this.age = age;
}
let p1 = new Person("张三",18);
// 为Person增加say方法
Person.prototype.say = function(){
}
// 这样写就会存在一个问题,当方法过多时,就会产生冗余代码
Person.prototype.s1 = function(){
}
Person.prototype.s2 = function(){
}
Person.prototype.s3 = function(){
}
Person.prototype.s4 = function(){
}
Person.prototype.s5 = function(){
}
// 为了减少这种重复,改良版
Person.prototype = {
constructor: Person,
a1:function(){
},
a2:function(){
},
a3:function(){
},
a4:function(){
},
a5:function(){
}
}
console.log(p1.s1); // 可以访问
console.log(p1.a1); // undefined
// 原因:p1对象在创建的时候已经有了一个确定的原型对象,就是旧的Person.prototype
// 由于Person.prototype后面被重新赋值,但是p1对象的原型对象没有改变,所以p1对象不能访问到新原型对象中的a1-a5方法
let p2 = new Person("李四",18);
console.log(p2.s1); // undefined Person.prototype被重新赋值,无s1方法
console.log(p2.a1); // 可以访问
var o1 = {
age: 2};
var o2 = o1;
o2.age = 18;
// 1、修改了o2对象的age属性
// 2、由于o2对象与o1对象是同一个对象
// 3、所以此时o1对象的age属性也被修改了
js实现拷贝继承
// 1、已经拥有了o3对象
var o3 = {
gender: "男", grade: "初三", group: "第五组", name: "张三"};
// 2、创建一个o3对象的拷贝(克隆):for...in循环
var o4 = {
};
// a、取出o3对象中的每一个属性
for (var key in o3) {
// key就是o3对象中的每一个属性
// b、获取到对应的属性值
var value = o3[key];
// c、把属性值放到o4中
o4[key] = value;
}
// 3、修改克隆对象,把该对象的name属性改为“李四“
o4.name = "李四";
console.log(o4); // 最终的目标对象结果
// 后续如果修改了o4对象中的相关属性,就不会影响到o3
对象扩展运算符
仿佛就是专门为了拷贝继承而生: var source = {
name: "张三", age: 15};
// 让target是一个新对象,同事拥有了name、age属性
var target = {
...source }
var target2 = {
...source, age:18}
var parent = {
age:18, gender:"男"};
var student = Object.create(parent);
student._proto_ === parent
var o1 = {
say: function(){
}}
var o2 = Object.create(o1);
function Animal(name,age,gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
function Person(name,age,gender,say) {
this.name = name;
this.age = age;
this.gender = gender;
this.say = function() {
console.log("你好");
};
}
function Animal(name,age,gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
function Person(name,age,gender,say) {
// 这段代码调用错误
// 为什么错误?
// 因为这种函数的调用方式,函数内部的this只能指向window
// Animal(name,age,gender);
// 目的:将Animal函数内部的this指向Person的实例
// Animal.call(this,name,age,gender);
// -->等价于:
Animal.apply(this,[name,age,gender]);
this.say = say;
}
var p1 = new Person("张三",18,"男",function(){
})
function createAnother(source) {
// 通过调用函数创建一个新对象
// Object.create()方法创建的对象时,属性是在原型下面的,也可以直接访问 anotherPerson.name
var clone = Object.create(source);
// 以某种方式来增强这个对象
clone.say = function() {
console.log("你好")
}
// 返回这个对象
return clone;
}
var Person = {
name: "张三",
age: 18
}
// 不仅有Person的所有属性还有自己的say方法
var anotherPerson = createAnother(Person);
console.log(anotherPerson); // {say: ƒ}
console.log(anotherPerson.__proto__); // {name: "张三",age:18}
console.log(anotherPerson.name); // 张三
function SuperType(name) {
this.name = name;
this.gender = "男";
}
SuperType.prototype.say = function() {
console.log(this.name)
}
function SubType(name, age) {
SuperType.call(this,name); //第二次调用SuperType()
this.age = age;
}
SubType.prototype = new SuperType(); // 第一次调用SuperType()
SubType.prototype.run = function(){
console.log(`${
this.name}在跑步`);
}
// 1、定义父类型
function SuperType(name) {
this.name = name;
this.gender = "男";
}
SuperType.prototype.say = function() {
console.log("name:",this.name);
}
// 2、定义继承方法
function inheritPrototype(subType,superType) {
// 创建对象
var protoType = Object.create(super.prototype);
// 增强对象
protoType.constructor = subType;
// 指定对象
subType.prototype = prototype;
}
// 3、 定义子类实现继承
function SubType(name,age) {
SuperType.call(this,age);
this.age = age;
}
//将子类SubType的原型指向父类SuperType原型的一个副本
//注意:要执行该动作后才能在SubType的prototype上定义方法,否则没用
inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge = function() {
console.log("age:",this.age);
}
// SubType的实例
var instance = new SubType("张三",18);
console.log(instance.name); //张三
console.log(instance.age); //18
instance.sayName(); // name: 张