【js高级 Day3】深入理解原型的方式继承,借用构造函数继承,组合继承,拷贝继承
01 課程介紹
02 復習
面向過程和面向對象都是編程的思想,方式不一樣
- 面向過程:凡事都是親力親為,所有的代碼都要自己寫,每一步都要很清楚,注重的是過程
- 面向對象:執(zhí)行者成為指揮者,只要找對象,然后讓對象做相關的事情,注重的是結果
面向對象的特性:封裝,繼承,多態(tài)
封裝;就是代碼的封裝,把一些特征和行為封裝在對象中.
面向對象的編程思想:根據(jù)需求,抽象出相關的對象,總結對象的特征和行為,把特征變成屬性,行為變成方法,然后定義(js)構造函數(shù),實例化對象,通過對象調(diào)用屬性和方法,完成相應的需求.—編程的思想
對象:具體特指的某個事物,有特征(屬性)和行為(方法)
如何創(chuàng)建對象?
- 通過調(diào)用new Object(),還有{},自定義構造函數(shù)
創(chuàng)建對象的方式
**1. 調(diào)用系統(tǒng)Object()----->創(chuàng)建出來的對象都是Object類型的,不能很明確的指出這個對象是屬于什么類型
- 字面量的方式{}----->只能創(chuàng)建一個對象(一次只能創(chuàng)建一個)**
3.工廠模式創(chuàng)建對象----->----->推論---->自定義構造函數(shù)的方式
自定義構造函數(shù)(優(yōu)化后的工廠模式)
自定義構造函數(shù)創(chuàng)建對象:4件事
1.在內(nèi)存中申請一塊空閑的空間,存儲創(chuàng)建的對象
2.this就是當前實例化的對象
3.設置對象中的屬性和方法(為對象添加屬性和方法,為屬性和方法賦值)
4.把創(chuàng)建后的對象返回
都是需要通過new的方式
什么是原型?
- 構造函數(shù)中有一個屬性prototype,是原型,程序員使用的
- 實例對象中有一個屬性__proto__,是原型,瀏覽器使用的,不是很標準的,
- 實例對象中的__proto__指向的就是該實例對象中的構造函數(shù)中的prototyp
- 構造函數(shù)中的prototype里面的屬性或者方法,可以直接通過實例對象調(diào)用
正常的寫法:
實例對象.__proto__才能訪問到構造函數(shù)中的prototype中的屬性或者方法
原型就是屬性,而這個屬性也是一個對象
Person.prototype--->是屬性 Person.prototype.屬性或者Person.ptototype.方法()本身在構造函數(shù)中定義的屬性和方法,當實例化對象的時候,實例對象中的屬性和方法都是在自己的空間中存在的,如果是多個對象。這些屬性和方法都會在單獨的空間中存在,浪費內(nèi)存空間,所以,為了數(shù)據(jù)共享,把想要節(jié)省空間的屬性或者方法寫在原型對象中,達到了數(shù)據(jù)共享,實現(xiàn)了節(jié)點內(nèi)存空間
function Person(name){this.name=name;}Person.prototype.sex="男";var per=new Person("小明");per.sexvar per2=new Person("小芳");per2.sex原型的作用之一:數(shù)據(jù)共享,節(jié)省內(nèi)存空間
原型的寫法:
- 構造函數(shù).prototype.屬性=值
- 構造函數(shù).prototype.方法=值---->函數(shù).prototype,函數(shù)也是對象,所以,里面也有__proto__
- 實例對象.prototype-------->實例對象中沒有這個屬性,只有__proto__(暫時的)
簡單的原型的寫法
缺陷:—>新的知識點---->原型直接指向{}---->就是一個對象,沒有構造器
通過原型為內(nèi)置對象添加原型的屬性或者方法----->原因:
系統(tǒng)的內(nèi)置對象的屬性和方法可能不滿足現(xiàn)在需求,所以,可以通過原型的方式加入屬性或者方法,為了方便開發(fā)
為內(nèi)置對象的原型中添加屬性和方法,那么這個內(nèi)置對象的實例對象就可以直接使用了
- String.prototype.方法=匿名函數(shù);
- var str=“哈哈”;
- str.方法();---->實例對象可以直接調(diào)用原型中的屬性或者方法
凡是string的實例對象都可以調(diào)用剛剛加入的方法
案例分析:
面向對象的思想來做: 分析對象,抽象出對象的特征和行為,定義構造函數(shù),屬性可以不共享
部分方法需要共享,方法加到prototype中定義(在原型中定義方法,數(shù)據(jù)共享,節(jié)省內(nèi)存空間)
食物對象(食物的橫縱坐標,寬和高,背景顏色)
食物需要畫出來—渲染出來–畫,隨機的畫,在畫食物的時候要先刪除原來的食物
小蛇對象(寬,高,方向)
蛇需要畫出來—渲染出來–畫,每走一次,需要把前一次的小蛇刪除
蛇走的時候,需要方向,是否吃到了食物
小蛇移動的時候,是否吃了食物(吃了就要把小蛇的后面加一個食物的寬和高,顏色,無非就是把原來的蛇尾復制了一個加入到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};//為內(nèi)置對象添加原型方法String.prototype.sayHi=function () {console.log("字符串的打招呼的方法");};//是一個實例對象var str="字符串";str.sayHi();//實例對象調(diào)用屬性或者方法,屬性或者方法肯定是在構造函數(shù)中或者是構造函數(shù)的原型中03 原型及原型鏈
使用對象---->使用對象中的屬性和對象中的方法,使用對象就要先有構造函數(shù)
構造函數(shù)
添加共享的屬性
Person.prototype.sex="男";添加共享的方法
Person.prototype.sayHi=function () {console.log("您好啊,怎么這么帥,就是這么帥");};實例化對象,并初始化
var per=new Person("小明",20);per.sayHi();如果想要使用一些屬性和方法,并且屬性的值在每個對象中都是一樣的,方法在每個對象中的操作也都是一樣,那么,為了共享數(shù)據(jù),節(jié)省內(nèi)存空間,是可以把屬性和方法通過原型的方式進行賦值
console.dir(per);//實例對象的結構console.dir(Person);//構造函數(shù)的結構實例對象的原型__proto__和構造函數(shù)的原型prototype指向是相同的
實例對象中的__proto__原型指向的是構造函數(shù)中的原型prototype
console.log(per.__proto__==Person.prototype);實例對象中__proto__是原型,瀏覽器使用的
構造函數(shù)中的prototype是原型,程序員使用的
原型鏈:是一種關系,實例對象和原型對象之間的關系,關系是通過原型(proto)來聯(lián)系的
04 原型的指向是否可以改變
構造函數(shù)中的this就是實例對象
原型對象中方法中的this就是實例對象
原型指向可以改變
- 實例對象的原型__proto__指向的是該對象所在的構造函數(shù)的原型對象
- 構造函數(shù)的原型對象(prototype)指向如果改變了,實例對象的原型(proto)指向也會發(fā)生改變
原型的指向是可以改變的
- 實例對象和原型對象之間的關系是通過__proto__原型來聯(lián)系起來的,這個關系就是原型鏈
05 原型最終指向了哪里
function Person() {}Person.prototype.eat=function () {console.log("吃東西");}; var per=new Person();console.dir(per);console.dir(Person);實例對象中有__proto__原型
構造函數(shù)中有prototype原型
prototype是對象
所以,prototype這個對象中也有__proto__,那么指向了哪里
實例對象中的__proto__指向的是構造函數(shù)的prototype
所以,prototype這個對象中__proto__指向的應該是某個構造函數(shù)的原型prototype
Person的prototype中的__proto__的指向
console.log(Person.prototype.proto);
per實例對象的__proto__------->Person.prototype的__proto__---->Object.prototype的__proto__是null
瀏覽器顯示結果:
06 原型指向改變?nèi)绾翁砑臃椒ê驮L問
//人的構造函數(shù)function Person(age) {this.age=age;}//人的原型中添加方法Person.prototype.eat=function () {console.log("人正在吃東西");};//學生構造函數(shù)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(); //人的構造函數(shù)function Person(age) {this.age=age;}//人的原型中添加方法Person.prototype.eat=function () {console.log("人正在吃東西");};//學生構造函數(shù)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是一門動態(tài)類型的語言,對象沒有什么,只要點了,那么這個對象就有了這個東西,沒有這個屬性,只要對象.屬性名字,對象就有這個屬性了,但是,該屬性沒有賦值,所以,結果是:undefined
console.log(per.fdsfdsfsdfds)console.log(fsdfdsfds);實例對象訪問這個屬性,應該先從實例對象中找,找到了就直接用,找不到就去指向的原型對象中找,找到了就使用,找不到呢?=====
通過實例對象能否改變原型對象中的屬性值?不能
就想改變原型對象中屬性的值,怎么辦?直接通過原型對象.屬性=值;可以改變
08 一個很神奇的原型鏈
原型鏈:實例對象和原型對象之間的關系,通過__proto__來聯(lián)系
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 繼承
面向對象編程思想:根據(jù)需求,分析對象,找到對象有什么特征和行為,通過代碼的方式來實現(xiàn)需求,要想實現(xiàn)這個需求,就要創(chuàng)建對象,要想創(chuàng)建對象,就應該顯示有構造函數(shù),然后通過構造函數(shù)來創(chuàng)建對象.,通過對象調(diào)用屬性和方法來實現(xiàn)相應的功能及需求,即可
首先JS不是一門面向對象的語言,JS是一門基于對象的語言,那么為什么學習js還要學習面向對象,因為面向對象的思想適合于人的想法,編程起來會更加的方便,及后期的維護…
面向對象的編程語言中有類(class)的概念(也是一種特殊的數(shù)據(jù)類型),但是JS不是面向對象的語言,所以,JS中沒有類(class),但是JS可以模擬面向對象的思想編程,JS中會通過構造函數(shù)來模擬類的概念(class)
小明,小紅,小麗,小白,小花 都是人
共同的特征和行為
特征—>屬性
行為—方法
面向對象的特性:封裝,繼承,多態(tài)
封裝:就是包裝
一個值存儲在一個變量中–封裝
一坨重復代碼放在一個函數(shù)中–封裝
一系列的屬性放在一個對象中–封裝
一些功能類似的函數(shù)(方法)放在一個對象中–封裝
好多相類似的對象放在一個js文件中—封裝
繼承: 首先繼承是一種關系,類(class)與類之間的關系,JS中沒有類,但是可以通過構造函數(shù)模擬類,然后通過原型來實現(xiàn)繼承
繼承也是為了數(shù)據(jù)共享,js中的繼承也是為了實現(xiàn)數(shù)據(jù)共享
原型作用之一:數(shù)據(jù)共享,節(jié)省內(nèi)存空間
原型作用之二:為了實現(xiàn)繼承
繼承是一種關系:
父類級別與類級別的關系
例子:
小楊—>人, 姓名, 有錢, 帥, 有功夫–降龍十八掌
小楊子–>人,
繼承:
姓氏----繼承
外表----繼承
財產(chǎn)----繼承
功夫—繼承
人: 姓名, 性別, 年齡 ,吃飯, 睡覺
學生類別: 姓名, 性別, 年齡 ,吃飯, 睡覺 學習行為
老師類別: 姓名, 性別, 年齡 ,吃飯, 睡覺 工資,教學行為
程序員: 姓名, 性別, 年齡 ,吃飯, 睡覺 工資, 敲代碼
司機類別: 姓名, 性別, 年齡 ,吃飯, 睡覺 工資 開車
動物類別: 體重, 顏色, 吃
狗類別: 體重,顏色, 吃, 咬人
二哈類別: 體重,顏色, 吃, 咬人 逗主人開心,汪汪,你好帥
多態(tài):一個對象有不同的行為,或者是同一個行為針對不同的對象,產(chǎn)生不同的結果,要想有多態(tài),就要先有繼承,js中可以模擬多態(tài),但是不會去使用,也不會模擬,
例子:
人,都有姓名,性別,年齡, 吃飯, 睡覺, 玩
學生,都有姓名,性別,年齡, 成績, 吃飯, 睡覺, 玩 ,學習的行為
js中通過原型來實現(xiàn)繼承
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;}//改變學生的原型的指向即可==========>學生和人已經(jīng)發(fā)生關系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 繼承案例
動物有名字,有體重,有吃東西的行為
小狗有名字,有體重,有毛色, 有吃東西的行為,還有咬人的行為
哈士奇名字,有體重,有毛色,性別, 有吃東西的行為,還有咬人的行為,逗主人開心的行為
11 借用構造函數(shù)
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;}//希望人的類別中的數(shù)據(jù)可以共享給學生---繼承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();為了數(shù)據(jù)共享,改變原型指向,做到了繼承—通過改變原型指向實現(xiàn)的繼承
缺陷:因為改變原型指向的同時實現(xiàn)繼承,直接初始化了屬性,繼承過來的屬性的值都是一樣的了,所以,這就是問題
只能重新調(diào)用對象的屬性進行重新賦值,
解決方案:繼承的時候,不用改變原型的指向,直接調(diào)用父級的構造函數(shù)的方式來為屬性賦值就可以了------借用構造函數(shù):把要繼承的父級的構造函數(shù)拿過來,使用一下就可以了
借用構造函數(shù):構造函數(shù)名字.call(當前對象,屬性,屬性,屬性…);
解決了屬性繼承,并且值不重復的問題
缺陷:父級類別中的方法不能繼承
12 組合繼承
原型實現(xiàn)繼承
借用構造函數(shù)實現(xiàn)繼承
組合繼承:原型繼承+借用構造函數(shù)繼承
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 總結繼承
面向對象特性:封裝,繼承,多態(tài)
繼承,類與類之間的關系,面向對象的語言的繼承是為了多態(tài)服務的,
js不是面向對象的語言,但是可以模擬面向對象.模擬繼承.為了節(jié)省內(nèi)存空間
繼承:
原型作用: 數(shù)據(jù)共享 ,目的是:為了節(jié)省內(nèi)存空間,
原型作用: 繼承 目的是:為了節(jié)省內(nèi)存空間
原型繼承:改變原型的指向
借用構造函數(shù)繼承:主要解決屬性的問題
組合繼承:原型繼承+借用構造函數(shù)繼承
既能解決屬性問題,又能解決方法問題
拷貝繼承:就是把對象中需要共享的屬性或者犯法,直接遍歷的方式復制到另一個對象中
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 函數(shù)的角色
//函數(shù)的角色: //函數(shù)的聲明 function f1() {console.log("我是函數(shù)"); } f1(); //函數(shù)表達式 var ff=function () {console.log("我也是一個函數(shù)"); }; ff();17 函數(shù)聲明和函數(shù)表達式的區(qū)別
//函數(shù)聲明 if(true){function f1() {console.log("哈哈,我又變帥了");}}else{function f1() {console.log("小蘇好猥瑣");}}f1(); //函數(shù)表達式var ff;if(true){ff=function () {console.log("哈哈,我又變帥了");};}else{ff=function () {console.log("小蘇好猥瑣");};}ff();函數(shù)聲明如果放在if-else的語句中,在IE8的瀏覽器中會出現(xiàn)問題
以后寧愿用函數(shù)表達式,都不用函數(shù)聲明
18 函數(shù)中的this指向的問題
函數(shù)中的this的指向
普通函數(shù)中的this是誰?-----window
對象.方法中的this是誰?----當前的實例對象
定時器方法中的this是誰?----window
構造函數(shù)中的this是誰?-----實例對象
原型對象方法中的this是誰?—實例對象
嚴格模式:
"use strict";//嚴格模式 function f1() {console.log(this);//window } f1(); 普通函數(shù) function f1() {console.log(this);} f1();定時器中的this
setInterval(function () {console.log(this);},1000);構造函數(shù)
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 函數(shù)的不同的調(diào)用方式
普通函數(shù)
function f1() {console.log("文能提筆控蘿莉"); } f1();構造函數(shù)—通過new 來調(diào)用,創(chuàng)建對象
function F1() {console.log("我是構造函數(shù),我驕傲"); } var f=new F1();對象的方法
function Person() {this.play=function () {console.log("玩代碼");};}var per=new Person();per.play();20 函數(shù)也是對象
函數(shù)是對象,對象不一定是函數(shù)
對象中有__proto__原型,是對象
函數(shù)中有prototype原型,是對象
- 對象中有__proto__,函數(shù)中應該有prototype
- 如果一個東西里面有prototype,又有__proto__,說明是函數(shù),也是對象
所有的函數(shù)實際上都是Function的構造函數(shù)創(chuàng)建出來的實例對象
var f1=new Function("num1","num2","return num1+num2");console.log(f1(10,20));console.log(f1.__proto__==Function.prototype);所以,函數(shù)實際上也是對象
console.dir(f1);console.dir(Function);21 數(shù)組的函數(shù)調(diào)用
數(shù)組可以存儲任何類型的數(shù)據(jù)
var arr=[function () {console.log("hls1183676168");},function () {console.log("hls1183676168");},function () {console.log("hls1183676168");},function () {console.log("hls1183676168");},function () {console.log("hls1183676168");}];回調(diào)函數(shù):函數(shù)作為參數(shù)使用
arr.forEach(function (ele) {ele(); });其他參考鏈接
【js高級 Day1】深入理解原型及作用,構造函數(shù)和實例對象和原型對象之間的關系
【js高級 Day2】深入理解原型添加方法,私有函數(shù),面向對象的編程思想(案例小貪吃蛇)
【js高級 Day3】深入理解原型的方式繼承,借用構造函數(shù)繼承,組合繼承,拷貝繼承
【js高級 Day4】深入理解apply和call方法,作用域,閉包,遞歸
【js高級 Day5】深入理解淺拷貝,深拷貝,遍歷DOM樹,正則表達式
【如果你是新手】推薦鏈接
【 js基礎 Day1】js的介紹及基本語法變量,運算符
【 js基礎 Day2】js的流程控制:分支語句,循環(huán).順序結構
【 js基礎 Day3】關鍵字的使用,數(shù)組(重點)和函數(shù)(重點)
【 js基礎 Day4】面向過程,面向對象,自定義對象,內(nèi)置對象
【 js基礎 Day5】函數(shù)(重點),作用域,預解析,arguments,對象
【 js基礎 Day6】內(nèi)置對象和基本包裝類型等知識
總結
以上是生活随笔為你收集整理的【js高级 Day3】深入理解原型的方式继承,借用构造函数继承,组合继承,拷贝继承的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Centos7 NAT网络连接方式以及N
- 下一篇: HPE Microserver GEN1