2020-12-29-继承和函数的进阶
作者:zhzh
时间: 2020-12-29
书籍/博客/视频:前端39期视频–JavaScript高级
网站地址:##
摘要
重点:JavaScript中继承的三种方法,
call函数的 使用
难点:原型和原型对象之间的关系
总结
继承的概念很容易理解,但就是在js中,原型和原型对象之间的关系有点点绕
目录
1、对象之间的继承:
1、用复制的方法实现王sc对王健林的属性的拷贝
var wjl = {
name : '王健林',
money: 10000000,
cars: ['玛莎拉蒂','特斯拉'],
houses:['b别墅','dabieshu '],
play:function () {
console.log('打高尔夫');
}
}
var wsc = {
name : '王cs',
}
//复制对象的成员给另一个对象
for (var key in wjl){
//不给wsc复制名字
if (wsc[key]){
continue;
}
wsc[key] = wjl[key];
}
console.dir(wsc);
2、把复制的代码封装到一个函数中,数据就不会只能是wsc和wjl了,对象的拷贝这里不是继承
var wjl = {
name : '王健林',
money: 10000000,
cars: ['玛莎拉蒂','特斯拉'],
houses:['b别墅','dabieshu '],
play:function () {
console.log('打高尔夫');
}
}
var wsc = {
name : '王cs',
}
function extend(parent,child) {
for (var key in parent){
//不给wsc复制名字
if (child[key]){
continue;
}
child[key] = parent[key];
}
}
extend(wjl,wsc);
console.dir(wsc);
2、对象拷贝的应用
1、原来的贪吃蛇案例是通过向数组创建一个和最后一个一样的对象
//贪吃蛇案例中 吃食物长大的部分
this.body.push({
x:last.x,
y:last.y,
color: last.color
})
2、通过调用创建的extend函数来,拷贝最后一个对象的属性
//创建一个新的对象
var obj = {};
//对象拷贝
extend(last,obj);
this.body.push(obj);
3、原型继承
继承:是类型和类型之间的关系
JavaScript中 本身并没有继承语法,都是 通过特殊的语法实现相同的效果,模拟继承的效果
把学生和老师的公共属性放到一个person类中
//parent:
function person() {
this.name = 'zs';
this.age = '';
this.sex = '';
}
function Student() {
// this.name = 'zs';
// this.age = '18';
// this.sex = '男';
this.score = 100;
}
//原型继承:无法设置构造函数的参数
Student.prototype = new person();
Student.prototype.constructor = Student;//给原型属性赋值的时候一定要设置constructor
//找不到再去原型属性中去找
var s1 = new Student();
原型链继承的缺点:
1、对原型中引用类型值的误修改:1.1实例中存在和原型对象上同名的属性时,会自动屏蔽原型对象上的同名属性;
1.2原型上任何类型的属性值都不会通过实例被重写,但是引用类型的属性值会受到
实例的影响而修改.
2、原型链不能实现子类向父类中传参.
4、call
call函数的作用和 bind 的作用类似,都是改变函数中this 的指向,并且返回一个新的函数(不可调用函数)
//bind 改变函数的this,并且返回一个新的函数
function fn(x,y) {
console.log(this);
console.log(x+y);
}
window.fn(5,6);
//改变成指定的
var o = {
name : 'zs'
};
//bind 改变函数的this,并且返回一个新的函数(不可调用)
var f = fn.bind(o,1,2);
f();
//call,也是改变函数中的this,直接调用函数
fn.call();
5、借用构造函数
*在子类函数中,通过call ( ) 方法调用父类函数后,子类实例 student, 可以访问到 Person 构造函数里的所有属性和方法。这样就实现了子类向父类的继承,解决了原型对象上对引用类型值的误修改操作
这里如果在实例对象中创建函数在 Student 中就无法访问到实例对象中的方法,如果作为属性放入person中,虽然调用是可以的,但是当每一个实例去调用它的时候就都会去拷贝一份方法,会消耗大量内存,而且同时当需求发生改变的时候就不能够及时的更新。
function person(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
// person.prototype.sayHi = function () {
// console.log('sayhi');
// }
//子类型
function Student(name,age,sex,score) {
//用call来改变person中this的指向
person.call(this,name,age,sex);//这里的this的指向是直接指向Student
this.socre = score;
}
var s1 = new Student('zs',18,'nan',100);
console.dir(s1);
6、组合继承,7继承的原型图
组合继承 = 原型继承 + 借用构造函数继承
//组合继承:借用构造函数 + 原型继承
function Person(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
Person.prototype.sayHi = function () {
console.log('大家好,我是' + this.name);
}
function Student(name,age,sex,score){
//借用构造函数
Person.call(this,name,age,sex);
this.socre = score;
}
//
Student.prototype = Person.prototype;
Student.prototype.constructor = Student;
Student.prototype.exam = function () {
console.log('考试');
}
//这里的person的原型和student的原型都是指向同一个地方,所有在子类型原型中添加一个方法后,其他的也能够调用
var s1 = new Student('zs',18,'nan',100);
console.dir(s1);
var p1 = new Person('ls',18,'男');
console.dir(p1);
function Teacher(name,age,sex,salary) {
Person.call(this,name,age,sex);
this.salary = salary;
}
Teacher.prototype = Person.prototype;
Teacher.prototype.constructor = Teacher;
var t1 = new Teacher('ww','nan','1W');
console.dir(t1);
输出结果:
上面这种方式就会导致Teacher在调用person.prototype的时候就会引用到exam方法
解决办法:
//通过原型,让子类型创建一个父类型的方法
Student.prototype = new Person();
输出的结果:
原型图:
基础的简单的原型对象:
后面 new 了一个方法后的对象,因为sayhi的方法在person的原型中所以会出现在t1的下一级的下一级,虽然这里t1对象中没有 SayHi 的方法但是在原型链上有sayhi的方法,就可以访问到