继承
j
子类获得父类已有的方法和属性;
作用:子类可以实用父类的属性和方法,并且可以添加自己新的
属性和方法,从而提高代码的复用性。
* 基类 派生类
原型继承
案例
function Animal(name){
this.name=name;
}
Animal.prototype.eat=function(){
console.log("Animal eat");
}
//原型继承实现的方法:用子类的原型对象指向父类的实例化
对象
Human.prototype=new Animal("凡凡");
function Human(id){
this.id=id;
}
Human.prototype.makeTools=function(){
console.log("Human makeTools");
}
let h=new Human("007");
h.name="jj";
console.log(h.name);
h.eat();
console.log(h.id);
h.makeTools();
//对象instanceof 类型:判断该对象是否为该类型,返回布尔值
console.log("h instanceof Human");//true 人是人
console.log("h instanceof Animal");//true 人是动物
let a =new Animal("jj");
console.log("a instanceof Human");//false 动物不是人
console.log("a instanceof Animal");//true 动物是动物
prototype
prototype属性:
对于类而言,prototype保存着所有实例的方法,即
所有的实例方法都是在prototype中保存着,平时我们在使用实
例方法时,虽然用对象直接调用,但是真正的保存是在
prototype中。在继承中prototype的作用更加明显;
我们创建的每个函数都有一个属性是prototype(原型)
,这是属性是一个指针,指向一个对象,该对象的用途是包含所
有实例共享的属性和方法。
案例1,原型方法的调用
function Student(newId,newName,newAge){
this.Id=newId;
this.Name=newName;
this.Age=newAge;
}
Student.prototype.sleep=function(){
console.log("晚上睡不着,上课睡最香")
};
let s=new Student(1,"大黄",18);
let s1=new Student(2,"小黄",19);
s.sleep();
//晚上睡不着,上课睡最香
s1.sleep();
//晚上睡不着,上课睡最香
console.log(s.Id,s.Nmae,s.Age);
//1,"大黄",18
console.log(s1.Id,s1.Nmae,s1.Age);
//2,"小黄",19
prototype 案例2
多个原型属性的区别
接着上个prototype案例
function Student(newId,newName,newAge){
this.Id=newId;
this.Nmae=newName;
this.newAge;
}
Student.prototype.tall="180";
Student.prototype.sleep=function(){
"晚上睡不着,上课睡最香"
};
let s=new Student(1,"大黄",18);
let s1=new Student(2,"小黄",19);
console.log(s.tall)//输出为180
s.tall="666";//相当给S的实例对象添加了新tall方法
属于个人行为不影响原型属性tall=180;
console.log(s.tall);//此时输出的是666;
delete s.tall;
console.log(s1.tall);//输出180;
prototype.案例3
给官方函数添加方法
依旧利用prototype;
Array.prototype.max=function(){
//求数组最大值;
let t=this[0];
for(var i=0;i<this,length;i++){
if(t<this[i]){
t=this[i];}
}
return t;
}
apply()和call()
每个函数dui都有两个非继承而来的方法apply()和call(),这
两个方法的用途都是来调用函数(在特定的作用域中),实际上
等于设置函数体内的this对象的值。调用函数实际上就是调用
该函数对象的call内部方法;
案例
function Monkey(newId,newName){
this.Id=newId;
this.Name=newName;
}
function Snake(newId,newName){
this.Id=newId;
this.Name=new Name;
}
function eat(str,str1){
consloe.log(this.Name+"吃"+str+"和"+str1);
}
let m=New Monkey(1,"泰山");
let s=New Snake(2,"小可爱");
eat.call(m,"香蕉","牛奶");
eat.call(s,"老鼠","青蛙");
eat.apply(m,["香蕉","牛奶"]);
eat.apply(s,["老鼠","青蛙"]);
//call()和apply()区别:apply的第二个参数得用[]括起来;
function fun(){
let m=New Monkey(1,"泰山");
eat.apply(m,arguments);
}
fun("香蕉","牛奶")
原型链继承
让子对象的原型指向父对象的实例,父对象的原型指向爷爷对
象的实例,依次类推,就形成了原型链
案例
<script>
function Animal(name){
this.name=name;
}
Animal.prototype.eat=function(){
console.log("Animal eat")
};
Human.prototype=new Animal("凡凡");
function Human(id){
this.id=id;
}
Human.prototype.makeTools=function(){
console.log("Human makeTools")
};
Student.prototype=new Human("007");
function Student(score){
this.score=score;
}
Student.prototype.study=function(){
console.log("Student study");
}
//原型链继承:访问属性的原理,现在自己的类中
查找属性或者方法,找不到就去找父类(向上查找)
let s=new Student(100);
console.log(s.name,s.id,s.score);
//输出为 凡凡 007 100
s.eat(); //输出Animal eat
s.makeTools();//输出Human makeTools
s.study();//输出Student study
原型链继承中的注意事项
//注意事项:
//1.无法在构造子类对象时,初始化由父类派生给子类的属性
//2.实现继承关系,必须在添加子类方法之前
//3.继承关系实现后,子类的原型对象不能再修改
深浅拷贝
内置类型与引用类型在内存中村春的区别
//内置基本类型:只有一块栈空间,保存的是变量的数值;
//引用类型:有两块空间,一块栈空间保存地址,一块堆
空间保存内容
---------------------------------------------
引用类型才有深拷贝和浅拷贝的概念
//浅拷贝:只拷贝内容但不开辟空间,两个变量共享一块堆空间
let arr1=[1,2,3];
let arr2=arr1;
arr1[0]="凡凡";
arr2[1]="潘玮柏"
console.log(arr1,arr2);
// 都是 凡凡 潘玮柏 3;
深拷贝
//深拷贝:开辟空间且赋值
let arr1=[1,2,3];
let arr2=[];
for(var i=0;i<arr1.length;i++){
arr2.push(arr1[i])
}
console.log(arr2);//输出结果为[1,2,3];
//..扩展运算符:赋值的是引用类型的内容,不再是地址
arr2=[...arr1];
arr1[0]="凡凡";
console.log(arr1,arr2);
//输出结果是 凡凡 ,2,3;
1,2,3;
function Student(name,id){
this.name=name;
this.id=id;
}
Student.protitype.copy=function(){
let item=new Student (this.name,this.id);
return item;
}
let s1=new Student("凡凡",1);
let s2=s1.copy();
s1.name="黄黄";
console.log(s1,s2);
// 黄黄,1;s1 修改了参数,生效
凡凡, 1;