1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 【js高级 Day3】深入理解原型的方式继承 借用构造函数继承 组合继承 拷贝继承

【js高级 Day3】深入理解原型的方式继承 借用构造函数继承 组合继承 拷贝继承

时间:2020-10-09 07:15:00

相关推荐

【js高级 Day3】深入理解原型的方式继承 借用构造函数继承 组合继承 拷贝继承

01 课程介绍

02 复习

面向过程和面向对象都是编程的思想,方式不一样

面向过程:凡事都是亲力亲为,所有的代码都要自己写,每一步都要很清楚,注重的是过程面向对象:执行者成为指挥者,只要找对象,然后让对象做相关的事情,注重的是结果

面向对象的特性:封装,继承,多态

封装;就是代码的封装,把一些特征和行为封装在对象中.

面向对象的编程思想:根据需求,抽象出相关的对象,总结对象的特征和行为,把特征变成属性,行为变成方法,然后定义(js)构造函数,实例化对象,通过对象调用属性和方法,完成相应的需求.—编程的思想

对象:具体特指的某个事物,有特征(属性)和行为(方法)

如何创建对象?

通过调用new Object(),还有{},自定义构造函数

创建对象的方式

**1. 调用系统Object()----->创建出来的对象都是Object类型的,不能很明确的指出这个对象是属于什么类型

字面量的方式{}----->只能创建一个对象(一次只能创建一个)**

3.工厂模式创建对象----->----->推论---->自定义构造函数的方式

自定义构造函数(优化后的工厂模式)

自定义构造函数创建对象:4件事

1.在内存中申请一块空闲的空间,存储创建的对象

2.this就是当前实例化的对象

3.设置对象中的属性和方法(为对象添加属性和方法,为属性和方法赋值)

4.把创建后的对象返回

都是需要通过new的方式

什么是原型?

构造函数中有一个属性prototype,是原型,程序员使用的实例对象中有一个属性__proto__,是原型,浏览器使用的,不是很标准的,实例对象中的__proto__指向的就是该实例对象中的构造函数中的prototyp构造函数中的prototype里面的属性或者方法,可以直接通过实例对象调用

正常的写法:

实例对象.__proto__才能访问到构造函数中的prototype中的属性或者方法

per.__proto__.eat();//__proto__不是标准的属性per.eat();

原型就是属性,而这个属性也是一个对象

Person.prototype--->是属性Person.prototype.属性或者Person.ptototype.方法()

本身在构造函数中定义的属性和方法,当实例化对象的时候,实例对象中的属性和方法都是在自己的空间中存在的,如果是多个对象。这些属性和方法都会在单独的空间中存在,浪费内存空间,所以,为了数据共享,把想要节省空间的属性或者方法写在原型对象中,达到了数据共享,实现了节点内存空间

function Person(name){this.name=name;}Person.prototype.sex="男";var per=new Person("小明");per.sexvar per2=new Person("小芳");per2.sex

原型的作用之一:数据共享,节省内存空间

原型的写法:

构造函数.prototype.属性=值构造函数.prototype.方法=值---->函数.prototype,函数也是对象,所以,里面也有__proto__实例对象.prototype-------->实例对象中没有这个属性,只有__proto__(暂时的)

简单的原型的写法

缺陷:—>新的知识点---->原型直接指向{}---->就是一个对象,没有构造器

构造函数.prototype={切记:如果这这种写法,要把构造器加上};

通过原型为内置对象添加原型的属性或者方法----->原因:

系统的内置对象的属性和方法可能不满足现在需求,所以,可以通过原型的方式加入属性或者方法,为了方便开发

为内置对象的原型中添加属性和方法,那么这个内置对象的实例对象就可以直接使用了

String.prototype.方法=匿名函数;var str=“哈哈”;str.方法();---->实例对象可以直接调用原型中的属性或者方法

String.prototype.fdsfdsf=function(){};

凡是string的实例对象都可以调用刚刚加入的方法

案例分析:

面向对象的思想来做: 分析对象,抽象出对象的特征和行为,定义构造函数,属性可以不共享

部分方法需要共享,方法加到prototype中定义(在原型中定义方法,数据共享,节省内存空间)

食物对象(食物的横纵坐标,宽和高,背景颜色)

食物需要画出来—渲染出来–画,随机的画,在画食物的时候要先删除原来的食物

小蛇对象(宽,高,方向)

蛇需要画出来—渲染出来–画,每走一次,需要把前一次的小蛇删除

蛇走的时候,需要方向,是否吃到了食物

小蛇移动的时候,是否吃了食物(吃了就要把小蛇的后面加一个食物的宽和高,颜色,无非就是把原来的蛇尾复制了一个加入到body中,------>把蛇尾拿出来再次加入到蛇尾的后面)

游戏对象(初始化食物,初始化小蛇,自动移动小蛇,判断按键)

自动的设置小蛇移动,判断小蛇是否撞墙,用户按下了什么方向键

window.变量=值;把这个局部变量的值暴露给window,成为了全局变量

function Person(age) {this.age=age;this.study=function () {}}Person.prototype.sex="男";//属性,属性在原型中Person.prototype.sayHi=function () {//方法,方法在原型中console.log("您好");};//实例化对象同时进行初始化var per=new Person(10);// console.log(per.__proto__.sex);// per.__proto__.sayHi();console.log(per.sex);per.sayHi();Person.prototype={//简单的原型的写法,缺少构造器constructor:Person};//为内置对象添加原型方法String.prototype.sayHi=function () {console.log("字符串的打招呼的方法");};//是一个实例对象var str="字符串";str.sayHi();//实例对象调用属性或者方法,属性或者方法肯定是在构造函数中或者是构造函数的原型中

03 原型及原型链

使用对象---->使用对象中的属性和对象中的方法,使用对象就要先有构造函数

构造函数

function Person(name,age) {//属性this.name=name;this.age=age;//在构造函数中的方法this.eat=function () {console.log("吃好吃的");};}

添加共享的属性

Person.prototype.sex="男";

添加共享的方法

Person.prototype.sayHi=function () {console.log("您好啊,怎么这么帅,就是这么帅");};

实例化对象,并初始化

var per=new Person("小明",20);per.sayHi();

如果想要使用一些属性和方法,并且属性的值在每个对象中都是一样的,方法在每个对象中的操作也都是一样,那么,为了共享数据,节省内存空间,是可以把属性和方法通过原型的方式进行赋值

console.dir(per);//实例对象的结构console.dir(Person);//构造函数的结构

实例对象的原型__proto__和构造函数的原型prototype指向是相同的

实例对象中的__proto__原型指向的是构造函数中的原型prototype

console.log(per.__proto__==Person.prototype);

实例对象中__proto__是原型,浏览器使用的

构造函数中的prototype是原型,程序员使用的

原型链:是一种关系,实例对象和原型对象之间的关系,关系是通过原型(proto)来联系的

04 原型的指向是否可以改变

构造函数中的this就是实例对象

原型对象中方法中的this就是实例对象

function Person(age) {this.age=age;console.log(this);}Person.prototype.eat=function () {console.log(this);console.log("您吃了没,走着,吃点臭豆腐去");};var per=new Person(10);per.eat();console.log(per);

function Student() {}Student.prototype.study=function () {console.log("就是天天学习,学习如何做人,如何敲代码,如何成为人");};Student.prototype={eat:function () {console.log("哈哈,好吃的榴莲酥");}};var stu=new Student();stu.eat();

//人的构造函数function Person(age) {this.age=10;}//人的原型对象方法Person.prototype.eat=function () {console.log("人的吃");};//学生的构造函数function Student() {}Student.prototype.sayHi=function () {console.log("嗨,小苏你好帅哦");};//学生的原型,指向了一个人的实例对象Student.prototype=new Person(10);var stu=new Student();stu.eat();stu.sayHi();

原型指向可以改变

实例对象的原型__proto__指向的是该对象所在的构造函数的原型对象构造函数的原型对象(prototype)指向如果改变了,实例对象的原型(proto)指向也会发生改变

原型的指向是可以改变的

实例对象和原型对象之间的关系是通过__proto__原型来联系起来的,这个关系就是原型链

05 原型最终指向了哪里

function Person() {}Person.prototype.eat=function () {console.log("吃东西");};

var per=new Person();console.dir(per);console.dir(Person);

实例对象中有__proto__原型

构造函数中有prototype原型

prototype是对象

所以,prototype这个对象中也有__proto__,那么指向了哪里

实例对象中的__proto__指向的是构造函数的prototype

所以,prototype这个对象中__proto__指向的应该是某个构造函数的原型prototype

Person的prototype中的__proto__的指向

console.log(Person.prototype.proto);

per实例对象的__proto__------->Person.prototype的__proto__---->Object.prototype的__proto__是null

console.log(per.__proto__==Person.prototype);console.log(per.__proto__.__proto__==Person.prototype.__proto__);console.log(Person.prototype.__proto__==Object.prototype);console.log(Object.prototype.__proto__);

浏览器显示结果:

06 原型指向改变如何添加方法和访问

//人的构造函数function Person(age) {this.age=age;}//人的原型中添加方法Person.prototype.eat=function () {console.log("人正在吃东西");};//学生构造函数function Student(sex) {this.sex=sex;}//学生的原型中添加方法----先在原型中添加方法Student.prototype.sayHi=function () {console.log("您好哦");};//改变了原型对象的指向Student.prototype=new Person(10);var stu=new Student("男");stu.eat();stu.sayHi();

//人的构造函数function Person(age) {this.age=age;}//人的原型中添加方法Person.prototype.eat=function () {console.log("人正在吃东西");};//学生构造函数function Student(sex) {this.sex=sex;}//改变了原型对象的指向Student.prototype=new Person(10);//学生的原型中添加方法----先在原型中添加方法Student.prototype.sayHi=function () {console.log("您好哦");};var stu=new Student("男");stu.eat();stu.sayHi();console.dir(stu);

如果原型指向改变了,那么就应该在原型改变指向之后添加原型方法

function Person(age) {this.age = age;}//指向改变了Person.prototype = {eat: function () {console.log("吃");}};//先添加原型方法Person.prototype.sayHi = function () {console.log("您好");};var per = new Person(10);per.sayHi();

07 实例对象的属性和原型对象中的属性重名问题

function Person(age,sex) {this.age=age;this.sex=sex;}Person.prototype.sex="女";var per=new Person(10,"男");console.log(per.sex);

因为JS是一门动态类型的语言,对象没有什么,只要点了,那么这个对象就有了这个东西,没有这个属性,只要对象.属性名字,对象就有这个属性了,但是,该属性没有赋值,所以,结果是:undefined

console.log(per.fdsfdsfsdfds)console.log(fsdfdsfds);

实例对象访问这个属性,应该先从实例对象中找,找到了就直接用,找不到就去指向的原型对象中找,找到了就使用,找不到呢?=====

通过实例对象能否改变原型对象中的属性值?不能

就想改变原型对象中属性的值,怎么办?直接通过原型对象.属性=值;可以改变

Person.prototype.sex="哦唛嘎的";per.sex="人";console.log(per.sex);console.dir(per);

08 一个很神奇的原型链

原型链:实例对象和原型对象之间的关系,通过__proto__来联系

var divObj=document.getElementById("dv");console.dir(divObj);

divObj.proto

---->HTMLDivElement.prototype的__proto__

—>HTMLElement.prototype的__proto__-

—>Element.prototype的__proto__---->Node.prototype的__proto__---->EventTarget.prototype的__proto__---->Object.prototype没有__proto__,所以,Object.prototype中的__proto__是null

09 继承

面向对象编程思想:根据需求,分析对象,找到对象有什么特征和行为,通过代码的方式来实现需求,要想实现这个需求,就要创建对象,要想创建对象,就应该显示有构造函数,然后通过构造函数来创建对象.,通过对象调用属性和方法来实现相应的功能及需求,即可

首先JS不是一门面向对象的语言,JS是一门基于对象的语言,那么为什么学习js还要学习面向对象,因为面向对象的思想适合于人的想法,编程起来会更加的方便,及后期的维护…

面向对象的编程语言中有类(class)的概念(也是一种特殊的数据类型),但是JS不是面向对象的语言,所以,JS中没有类(class),但是JS可以模拟面向对象的思想编程,JS中会通过构造函数来模拟类的概念(class)

小明,小红,小丽,小白,小花 都是人

共同的特征和行为

特征—>属性

行为—方法

面向对象的特性:封装,继承,多态

封装:就是包装

一个值存储在一个变量中–封装

一坨重复代码放在一个函数中–封装

一系列的属性放在一个对象中–封装

一些功能类似的函数(方法)放在一个对象中–封装

好多相类似的对象放在一个js文件中—封装

继承: 首先继承是一种关系,类(class)与类之间的关系,JS中没有类,但是可以通过构造函数模拟类,然后通过原型来实现继承

继承也是为了数据共享,js中的继承也是为了实现数据共享

原型作用之一:数据共享,节省内存空间

原型作用之二:为了实现继承

继承是一种关系:

父类级别与类级别的关系

例子:

小杨—>人, 姓名, 有钱, 帅, 有功夫–降龙十八掌

小杨子–>人,

继承:

姓氏----继承

外表----继承

财产----继承

功夫—继承

人: 姓名, 性别, 年龄 ,吃饭, 睡觉

学生类别: 姓名, 性别, 年龄 ,吃饭, 睡觉 学习行为

老师类别: 姓名, 性别, 年龄 ,吃饭, 睡觉 工资,教学行为

程序员: 姓名, 性别, 年龄 ,吃饭, 睡觉 工资, 敲代码

司机类别: 姓名, 性别, 年龄 ,吃饭, 睡觉 工资 开车

动物类别: 体重, 颜色, 吃

狗类别: 体重,颜色, 吃, 咬人

二哈类别: 体重,颜色, 吃, 咬人 逗主人开心,汪汪,你好帅

多态:一个对象有不同的行为,或者是同一个行为针对不同的对象,产生不同的结果,要想有多态,就要先有继承,js中可以模拟多态,但是不会去使用,也不会模拟,

例子:

人,都有姓名,性别,年龄, 吃饭, 睡觉, 玩

学生,都有姓名,性别,年龄, 成绩, 吃饭, 睡觉, 玩 ,学习的行为

js中通过原型来实现继承

function Person(name,age,sex) {this.name=name;this.sex=sex;this.age=age;}Person.prototype.eat=function () {console.log("人可以吃东西");};Person.prototype.sleep=function () {console.log("人在睡觉");};Person.prototype.play=function () {console.log("生活就是不一样的玩法而已");};function Student(score) {this.score=score;}//改变学生的原型的指向即可==========>学生和人已经发生关系Student.prototype=new Person("小明",10,"男");Student.prototype.study=function () {console.log("学习很累很累的哦.");};//相同的代码太多,造成了代码的冗余(重复的代码)var stu=new Student(100);console.log(stu.name);console.log(stu.age);console.log(stu.sex);stu.eat();stu.play();stu.sleep();console.log("下面的是学生对象中自己有的");console.log(stu.score);stu.study();

10 继承案例

动物有名字,有体重,有吃东西的行为

小狗有名字,有体重,有毛色, 有吃东西的行为,还有咬人的行为

哈士奇名字,有体重,有毛色,性别, 有吃东西的行为,还有咬人的行为,逗主人开心的行为

//动物的构造韩素function Animal(name,weight) {this.name=name;this.weight=weight;}//动物的原型的方法Animal.prototype.eat=function () {console.log("天天吃东西,就是吃");};//狗的构造函数function Dog(color) {this.color=color;}Dog.prototype=new Animal("哮天犬","50kg");Dog.prototype.bitePerson=function () {console.log("哼~汪汪~咬死你");};//哈士奇function ErHa(sex) {this.sex=sex;}ErHa.prototype=new Dog("黑白色");ErHa.prototype.playHost=function () {console.log("哈哈~要坏衣服,要坏桌子,拆家..嘎嘎...好玩,开心不,惊喜不,意外不");};var erHa=new ErHa("雄性");console.log(erHa.name,erHa.weight,erHa.color);erHa.eat();erHa.bitePerson();erHa.playHost();

11 借用构造函数

function Person(name,age,sex,weight) {this.name=name;this.age=age;this.sex=sex;this.weight=weight;}Person.prototype.sayHi=function () {console.log("您好");};function Student(score) {this.score=score;}//希望人的类别中的数据可以共享给学生---继承Student.prototype=new Person("小明",10,"男","50kg");var stu1=new Student("100");console.log(stu1.name,stu1.age,stu1.sex,stu1.weight,stu1.score);stu1.sayHi();var stu2=new Student("120");stu2.name="张三";stu2.age=20;stu2.sex="女";console.log(stu2.name,stu2.age,stu2.sex,stu2.weight,stu2.score);stu2.sayHi();var stu3=new Student("130");console.log(stu3.name,stu3.age,stu3.sex,stu3.weight,stu3.score);stu3.sayHi();

为了数据共享,改变原型指向,做到了继承—通过改变原型指向实现的继承

缺陷:因为改变原型指向的同时实现继承,直接初始化了属性,继承过来的属性的值都是一样的了,所以,这就是问题

只能重新调用对象的属性进行重新赋值,

解决方案:继承的时候,不用改变原型的指向,直接调用父级的构造函数的方式来为属性赋值就可以了------借用构造函数:把要继承的父级的构造函数拿过来,使用一下就可以了

借用构造函数:构造函数名字.call(当前对象,属性,属性,属性…);

解决了属性继承,并且值不重复的问题

缺陷:父级类别中的方法不能继承

function Person(name, age, sex, weight) {this.name = name;this.age = age;this.sex = sex;this.weight = weight;}Person.prototype.sayHi = function () {console.log("您好");};function Student(name,age,sex,weight,score) {//借用构造函数Person.call(this,name,age,sex,weight);this.score = score;}var stu1 = new Student("小明",10,"男","10kg","100");console.log(stu1.name, stu1.age, stu1.sex, stu1.weight, stu1.score);var stu2 = new Student("小红",20,"女","20kg","120");console.log(stu2.name, stu2.age, stu2.sex, stu2.weight, stu2.score);var stu3 = new Student("小丽",30,"妖","30kg","130");console.log(stu3.name, stu3.age, stu3.sex, stu3.weight, stu3.score);

12 组合继承

原型实现继承

借用构造函数实现继承

组合继承:原型继承+借用构造函数继承

function Person(name,age,sex) {this.name=name;this.age=age;this.sex=sex;}Person.prototype.sayHi=function () {console.log("阿涅哈斯诶呦");};function Student(name,age,sex,score) {//借用构造函数:属性值重复的问题Person.call(this,name,age,sex);this.score=score;}//改变原型指向----继承Student.prototype=new Person();//不传值Student.prototype.eat=function () {console.log("吃东西");};var stu=new Student("小黑",20,"男","100分");console.log(stu.name,stu.age,stu.sex,stu.score);stu.sayHi();stu.eat();var stu2=new Student("小黑黑",200,"男人","1010分");console.log(stu2.name,stu2.age,stu2.sex,stu2.score);stu2.sayHi();stu2.eat();//属性和方法都被继承了

13 拷贝继承

拷贝继承;把一个对象中的属性或者方法直接复制到另一个对象中

var obj1={name:"小糊涂",age:20,sleep:function () {console.log("睡觉了");}};//改变了地址的指向var obj2=obj1;console.log(obj2.name,obj2.age);obj2.sleep();var obj1={name:"小糊涂",age:20,sleep:function () {console.log("睡觉了");}}; var obj2={};for(var key in obj1){obj2[key]=obj1[key];}console.log(obj2.name);function Person() {}Person.prototype.age=10;Person.prototype.sex="男";Person.prototype.height=100;Person.prototype.play=function () {console.log("玩的好开心");};var obj2={};

Person的构造中有原型prototype,prototype就是一个对象,那么里面,age,sex,height,play都是该对象中的属性或者方法

for(var key in Person.prototype){obj2[key]=Person.prototype[key];}console.dir(obj2);obj2.play();

14 总结继承

面向对象特性:封装,继承,多态

继承,类与类之间的关系,面向对象的语言的继承是为了多态服务的,

js不是面向对象的语言,但是可以模拟面向对象.模拟继承.为了节省内存空间

继承:

原型作用: 数据共享 ,目的是:为了节省内存空间,

原型作用: 继承 目的是:为了节省内存空间

原型继承:改变原型的指向

借用构造函数继承:主要解决属性的问题

组合继承:原型继承+借用构造函数继承

既能解决属性问题,又能解决方法问题

拷贝继承:就是把对象中需要共享的属性或者犯法,直接遍历的方式复制到另一个对象中

15 逆推继承看原型

function F1(age) {this.age = age;}function F2(age) {this.age = age;}F2.prototype = new F1(10);function F3(age) {this.age = age;}F3.prototype = new F2(20);var f3 = new F3(30);console.log(f3.age);//

16 函数的角色

//函数的角色://函数的声明function f1() {console.log("我是函数");}f1();//函数表达式var ff=function () {console.log("我也是一个函数");};ff();

17 函数声明和函数表达式的区别

//函数声明if(true){function f1() {console.log("哈哈,我又变帅了");}}else{function f1() {console.log("小苏好猥琐");}}f1();

//函数表达式var ff;if(true){ff=function () {console.log("哈哈,我又变帅了");};}else{ff=function () {console.log("小苏好猥琐");};}ff();

函数声明如果放在if-else的语句中,在IE8的浏览器中会出现问题

以后宁愿用函数表达式,都不用函数声明

18 函数中的this指向的问题

函数中的this的指向

普通函数中的this是谁?-----window

对象.方法中的this是谁?----当前的实例对象

定时器方法中的this是谁?----window

构造函数中的this是谁?-----实例对象

原型对象方法中的this是谁?—实例对象

严格模式:

"use strict";//严格模式function f1() {console.log(this);//window}f1();

普通函数

function f1() {console.log(this);}f1();

定时器中的this

setInterval(function () {console.log(this);},1000);

构造函数

function Person() {console.log(this);

对象的方法

this.sayHi=function () {console.log(this);};}

原型中的方法

Person.prototype.eat=function () {console.log(this);};var per=new Person();console.log(per);per.sayHi();per.eat();

BOM:中顶级对象是window,浏览器中所有的东西都是window的

19 函数的不同的调用方式

普通函数

function f1() {console.log("文能提笔控萝莉");}f1();

构造函数—通过new 来调用,创建对象

function F1() {console.log("我是构造函数,我骄傲");}var f=new F1();

对象的方法

function Person() {this.play=function () {console.log("玩代码");};}var per=new Person();per.play();

20 函数也是对象

函数是对象,对象不一定是函数

对象中有__proto__原型,是对象

函数中有prototype原型,是对象

function F1() {}console.dir(F1);console.dir(Math);//中有__proto__,但是没有prorotype

对象中有__proto__,函数中应该有prototype如果一个东西里面有prototype,又有__proto__,说明是函数,也是对象

function F1(name) {this.name=name;}console.dir(F1);

所有的函数实际上都是Function的构造函数创建出来的实例对象

var f1=new Function("num1","num2","return num1+num2");console.log(f1(10,20));console.log(f1.__proto__==Function.prototype);

所以,函数实际上也是对象

console.dir(f1);console.dir(Function);

21 数组的函数调用

数组可以存储任何类型的数据

var arr=[function () {console.log("hls1183676168");},function () {console.log("hls1183676168");},function () {console.log("hls1183676168");},function () {console.log("hls1183676168");},function () {console.log("hls1183676168");}];

回调函数:函数作为参数使用

arr.forEach(function (ele) {ele();});

其他参考链接

【js高级 Day1】深入理解原型及作用,构造函数和实例对象和原型对象之间的关系

【js高级 Day2】深入理解原型添加方法,私有函数,面向对象的编程思想(案例小贪吃蛇)

【js高级 Day3】深入理解原型的方式继承,借用构造函数继承,组合继承,拷贝继承

【js高级 Day4】深入理解apply和call方法,作用域,闭包,递归

【js高级 Day5】深入理解浅拷贝,深拷贝,遍历DOM树,正则表达式

【如果你是新手】推荐链接

【 js基础 Day1】js的介绍及基本语法变量,运算符

【 js基础 Day2】js的流程控制:分支语句,循环.顺序结构

【 js基础 Day3】关键字的使用,数组(重点)和函数(重点)

【 js基础 Day4】面向过程,面向对象,自定义对象,内置对象

【 js基础 Day5】函数(重点),作用域,预解析,arguments,对象

【 js基础 Day6】内置对象和基本包装类型等知识

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。