摘要:一面向對象概念面向對象就是使用對象。因此在構造函數(shù)中表示剛剛創(chuàng)建出來的對象。在構造函數(shù)中利用對象的動態(tài)特性為其對象添加成員。
一、面向對象 1.1 概念
面向對象就是使用對象。面向對象開發(fā)就是使用對象開發(fā)。
面向過程就是用過程的方式進行開發(fā)。面向對象是對面向過程的封裝。
1.2 三大特性抽象性
所謂的抽象性就是:如果需要一個對象描述數(shù)據(jù),需要抽取這個對象的核心數(shù)據(jù)
提出需要的核心屬性和方法
不在特定的環(huán)境下無法明確對象的具體意義
封裝性
對象是將數(shù)據(jù)與功能組合到一起,即封裝
JS對象就是鍵值對的集合,鍵值如果是數(shù)據(jù)(基本數(shù)據(jù)、符合數(shù)據(jù)、空數(shù)據(jù))就稱為屬性,如果鍵值是函數(shù)那么就稱為方法
對象就是將屬性與方法封裝起來
方法是將過程封裝起來
繼承性
所謂繼承性就是自己沒有但是別人有,拿過來成為自己的,就是繼承,繼承是實現(xiàn)復用的一種手段
在Java等語言中繼承滿足一個class的規(guī)則,類是一個class,他規(guī)定了一個對象有什么屬性和方法。
在這些語言中繼承是class之間的繼承,一個class繼承另一個class,那么該class就有了另一個class的成員,那么由該class創(chuàng)建出來的對象就同時具有兩個class的成員。
在JS中沒有明確的繼承語法(ES6提供了class extend語法),一般都是按照繼承的理念實現(xiàn)對象的成員擴充實現(xiàn)繼承,因此JS中實現(xiàn)繼承的方法非常對多。
傳統(tǒng)繼承基于類,JS繼承基于對象
一個簡單的繼承模式:混入(mix)
function mix ( o1, o2 ) { for ( var k in o2 ) { o1[ k ] = o2[ k ]; } }1.3 關于面向對象的一些其他概念
類class:在JS中就是構造函數(shù)
在傳統(tǒng)的面向對象語言中,使用一個叫類的東西定義模板,然后使用模板創(chuàng)建對象。
在構造方法中也具有類似的功能,因此也稱其為類
實例(instance)與對象(object)
實例一般是指某一個構造函數(shù)創(chuàng)建出來的對象,我們稱為XXXX 構造函數(shù)的實例
實例就是對象。對象是一個泛稱
實例與對象是一個近義詞
鍵值對與屬性和方法
在JS中鍵值對的集合稱為對象
如果值為數(shù)據(jù)(非函數(shù)),就稱該鍵值對為屬性
如果值為函數(shù)(方法),就稱該鍵值對為方法method
父類與子類(基類和派生類)
傳統(tǒng)的面向對象語言中使用類來實現(xiàn)繼承那么就有父類、子類的概念
父類又稱為基類,子類又稱為派生類
在JS中沒有類的概念,在JS中常常稱為父對象,子對象,基對象,派生對象。
二、構造函數(shù) 2.1 構造函數(shù)是干什么用的初始化數(shù)據(jù)的
在JS中給對象添加屬性用的,初始化屬性值用的
2.2 創(chuàng)建對象的過程代碼:var p = new Person();
首先運算符new創(chuàng)建了一個對象,類似于{},是一個沒有任何(自定義)成員的對象。
使用new 創(chuàng)建對象,那么對象的類型就是創(chuàng)建他的構造函數(shù)名
使用{}無論如何都是Object類型,相當于new Object
然后調用構造函數(shù),為其初始化成員
構造函數(shù)在調用的一開始,有一個賦值操作,即this = 剛剛創(chuàng)建出來的對象。
因此在構造函數(shù)中this表示剛剛創(chuàng)建出來的對象。
在構造函數(shù)中 利用 對象的動態(tài)特性 為其對象添加成員。
三、作用域 3.1 什么是作用域域表示的就是范圍,即作用域,就是一個名字在什么地方可以使用,什么時候不能使用。
簡單的說,作用域是針對變量的,比如我們創(chuàng)建一個函數(shù) a1,函數(shù)里面又包了一個子函數(shù) a2。
// 全局作用域 functiona a1() { // a1作用域 function a2() { // a2作用域 } }
此時就存 在三個作用域:全局作用域,a1 作用域,a2 作用域;即全局作用域包含了 a1 的作用域,a2 的作用域包含了 a1 的作用域。
當 a2 在查找變量的時候會先從自身的作用域區(qū)查找,找不到再到上一級 a1 的作用域查找,如果還沒找到就
到全局作用域區(qū)查找,這樣就形成了一個作用域鏈。
函數(shù)允許訪問函數(shù)外部的數(shù)據(jù)
整個代碼結構中只有函數(shù)可以限定作用域
作用規(guī)則首先使用提升規(guī)則分析
如果當前作用域中有了名字了,就不考慮外面的名字
3.3 屬性搜索原則所謂的屬性搜索原則,就是對象在訪問屬性或方法的時候,首先在當前對象中查找
如果當前對象中存儲著屬性或方法,停止查找,直接使用該屬性或方法
如果當前對象沒有該成員,那么再在其原型對象中查找
如果原型對象中含有該成員,那么停止查找,直接使用
如果原型中還沒有,就到原型的原型中查找
如此往復,直到Object.protitype還沒有,那么就返回undefined
如果是調用方法就報錯,該xxx不是一個函數(shù)
四、閉包 4.1 說說你對閉包的理解實用閉包主要是為了設計私有方法和變量。閉包的優(yōu)點是可以避免全局變量的污染;缺點是閉包會常駐內存,增加內存使用量,使用不當很容易造成內存泄露。在JavaScript中,函數(shù)即閉包,只有函數(shù)才能產(chǎn)生作用域。
閉包有3個特性:
函數(shù)嵌套函數(shù)
在函數(shù)內部可以引用外部的參數(shù)和變量
參數(shù)和變量不會以垃圾回收機制回收
4.2 閉包有什么用(特性)閉包的作用,就是保存自己私有的變量,通過提供的接口(方法)給外部使用,但外部不能直接訪問該變量。
通過使用閉包,我們可以做很多事情,比如模擬面向對象的代碼風格;更優(yōu)雅,更簡潔的表達出代碼;在某些方面提升代碼的執(zhí)行效率。利用閉包可以實現(xiàn)如下需求:
匿名自執(zhí)行函數(shù)
一個匿名的函數(shù),并立即執(zhí)行它,由于外部無法引用它內部的變量,因此在執(zhí)行完后很快就會被釋放,關鍵是這種機制不會污染全局對象。
緩存
閉包正是可以做到這一點,因為它不會釋放外部的引用,從而函數(shù)內部的值可以得以保留。
實現(xiàn)封裝
模擬面向對象的代碼風格
4.3 閉包的基本模型對象模式
函數(shù)內部定義個一個對象,對象中綁定多個函數(shù)(方法),返回對象,利用對象的方法訪問函數(shù)內的數(shù)據(jù)
function createPerson() { var __name__ = ""; return { getName: function () { return __name__; }, setName: function( value ) { // 如果不姓張就報錯 if ( value.charAt(0) === "張" ) { __name__ = value; } else { throw new Error( "姓氏不對,不能取名" ); } } } } var p = createPerson(); p.set_Name( "張三豐" ); console.log( p.get_Name() ); p.set_Name( "張王富貴" ); console.log( p.get_Name() );
函數(shù)模式
函數(shù)內部定義一個新函數(shù),返回新函數(shù),用新函數(shù)獲得函數(shù)內的數(shù)據(jù)
function foo() { var num = Math.random(); function func() { return mun; } return func; } var f = foo(); // f 可以直接訪問這個 num var res1 = f(); var res2 = f();
沙箱模式
沙箱模式就是一個自調用函數(shù),代碼寫到函數(shù)中一樣會執(zhí)行,但是不會與外界有任何的影響,比如jQuery
(function () { var jQuery = function () { // 所有的算法 } // .... // .... jQuery.each = function () {} window.jQuery = window.$ = jQuery; })(); $.each( ... )4.4 閉包的性能問題
js 垃圾回收機制,也就是當一個函數(shù)被執(zhí)行完后,其作用域會被收回,如果形成了閉包,執(zhí)行完后其作用域就不會被收回。
函數(shù)執(zhí)行需要內存,那么函數(shù)中定義的變量,會在函數(shù)執(zhí)行結束后自動回收,凡是因為閉包結構的,被引出的數(shù)據(jù),如果還有變量引用這些數(shù)據(jù)的話,那么這些數(shù)據(jù)就不會被回收。因此在使用閉包的時候如果不使用某些數(shù)據(jù)了,一定要賦值一個null
var f = (function () { var num = 123; return function () { return num; }; })(); // f 引用著函數(shù),函數(shù)引用著變量num // 因此在不使用該數(shù)據(jù)的時候,最好寫上 f = null;五、原型 5.1 什么是原型
一句話說明什么是原型:原型能存儲我們的方法,構造函數(shù)創(chuàng)建出來的實例對象能夠引用原型中的方法。
JS中一切皆對象,而每個對象都有一個原型(Object除外),這個原型,大概就像Java中的父類,所以,基本上你可以認為原型就是這個對象的父對象,即每一個對象(Object除外)內部都保存了它自己的父對象,這個父對象就是原型。一般創(chuàng)建的對象如果沒有特別指定原型,那么它的原型就是Object(這就很類似Java中所有的類默認繼承自Object類)。
ES6通過引入class ,extends等關鍵字,以一種語法糖的形式把構造函數(shù)包裝成類的概念,更便于大家理解。是希望開發(fā)者不再花精力去關注原型以及原型鏈,也充分說明原型的設計意圖和類是一樣的。
5.2 查看對象的原型當對象被創(chuàng)建之后,查看它們的原型的方法不止一種,以前一般使用對象的__proto__屬性,ES6推出后,推薦用Object.getPrototypeOf()方法來獲取對象的原型
function A(){ this.name="lala"; } var a=new A(); console.log(a.__proto__) //輸出:Object {} //推薦使用這種方式獲取對象的原型 console.log(Object.getPrototypeOf(a)) //輸出:Object {}
無論對象是如何創(chuàng)建的,默認原型都是Object,在這里需要提及的比較特殊的一點就是,通過構造函數(shù)來創(chuàng)建對象,函數(shù)A本身也是一個對象,而A有兩個指向表示原型的屬性,分別是__proto__和prototype,而且兩個屬性并不相同
function A(){ this.name="lala"; } var a=new A(); console.log(A.prototype) //輸出:Object {} console.log(A.__proto__) //輸出:function () {} console.log(Object.getPrototypeOf(A)) //輸出:function () {}
函數(shù)的的prototype屬性只有在當作構造函數(shù)創(chuàng)建的時候,把自身的prototype屬性值賦給對象的原型。而實際上,作為函數(shù)本身,它的原型應該是function對象,然后function對象的原型才是Object。
總之,建議使用ES6推薦的查看原型和設置原型的方法。
5.3 原型的用法其實原型和類的繼承的用法是一致的:當你想用某個對象的屬性時,將當前對象的原型指向該對象,你就擁有了該對象的使用權了。
function A(){ this.name="world "; } function B(){ this.bb="hello" } var a=new A(); var b=new B(); //將b設置為a的原型,此處有一個問題,即a的constructor也指向了B構造函數(shù),可能需要糾正 Object.setPrototypeOf(a,b); a.constructor=A; console.log(a.bb); //hello
如果使用ES6來做的話則簡單許多,甚至不涉及到prototype這個屬性
class B{ constructor(){ this.bb="hello" } } class A extends B{ constructor(){ super(); this.name="world"; } } var a=new A(); console.log(a.bb+" "+a.name); //hello world console.log(typeof(A)) //"function"
怎么樣?是不是已經(jīng)完全看不到原型的影子了?活脫脫就是類繼承,但是你也看得到實際上類A 的類型是function,所以說,本質上class在JS中是一種語法糖,JS繼承的本質依然是原型,不過,ES6引入class,extends 來掩蓋原型的概念也是一個很友好的舉動,對于長期學習那些類繼承為基礎的面對對象編程語言的程序員而言。
我的建議是,盡可能理解原型,盡可能用class這種語法糖。
好了,問自己兩個問題:
為什么要使用原型?——提高函數(shù)的復用性。
為什么屬性不放在原型上而方法要放在原型上?
利用對象的動態(tài)特性:構造函數(shù).prototype.xxxx = vvv
利用直接替換
Student.prototype = { sayHello : function(){}, study : function(){} };5.4 原型鏈
什么是原型鏈?
凡是對象就有原型,那么原型又是對象,因此凡是給定一個對象,那么就可以找到他的原型,原型還有原型,那么如此下去,就構成一個對象的序列,稱該結構為原型鏈。
每個實例對象都有一個__proto_屬性,該屬性指向它原型對象,這個實例對象 的構造函數(shù)有一個原型屬性 prototype,與實例的__proto__屬性指向同一個對象。當一個對象在查找一個屬性的時, 自身沒有就會根據(jù)__proto__ 向它的原型進行查找,如果都沒有,則向它的原型的原型繼續(xù)查找,直到查到 Object.prototype._proto_為 null,這樣也就形成了原型鏈。
這個概念其實也變得比較簡單,可以類比類的繼承鏈條,即每個對象的原型往上追溯,一直到Object為止,這組成了一個鏈條,將其中的對象串聯(lián)起來,當查找當前對象的屬性時,如果沒找到,就會沿著這個鏈條去查找,一直到Object,如果還沒發(fā)現(xiàn),就會報undefined。
原型鏈的結構
凡是使用構造函數(shù),創(chuàng)建出對象,并且沒有利用賦值的方式修改原型,就說該對象保留默認的原型鏈。
默認原型鏈結構是什么樣子呢?
function Person(){} var p = new Person(); //p 具有默認的原型鏈
默認的原型鏈結構就是:當前對象 -> 構造函數(shù).prototype -> Object.prototype -> null
在實現(xiàn)繼承的時候,有時候會利用替換原型鏈結構的方式實現(xiàn)原型繼承,那么原型鏈結構就會發(fā)生改變
function DunizbCollection(){} DunizbCollection.prototype = []; var arr = new DunizbCollection();
此時arr對象的原型鏈結構被指向了數(shù)組對象的原型鏈結構了:arr -> [] -> Array.prototype -> Object.prototype -> null
用圖形表示對象的原型鏈結構
以如下代碼為例繪制原型鏈結構
function Person(){} var p = new Person();
原型鏈結構圖為:
使用原型需要注意兩點:
原型繼承鏈條不要太長,否則會出現(xiàn)效率問題。
指定原型時,注意constructor也會改變。
六、繼承實現(xiàn)繼承有兩種常見方式:
6.1 混合式繼承最簡單的繼承就是將別的對象的屬性強加到我身上,那么我就有這個成員了。
混合式繼承的簡單描述:
function Person() {}; Person.prototype.extend = function ( o ) { for ( var k in o ) { this[ k ] = o[ k ]; } }; Person.prototype.extend({ run: function () { console.log( "我能跑了" ); }, eat: function () { console.log( "我可以吃了" ); }, sayHello: function () { console.log( "我吃飽了" ); } });6.2 原型繼承
利用原型也可以實現(xiàn)繼承,不需要在我身上添加任何成員,只要原型有了我就有了。
6.3 借用構造函數(shù)繼承這種技術的基本思想相當簡單,即在子類型構造函數(shù)的內部調用超類型構造函數(shù),而函數(shù)只不過是在特定環(huán)境中執(zhí)行代碼的對象,因此通過使用apply()和call()方法也可以在(將來)新創(chuàng)建的對象上執(zhí)行構造函數(shù)
function Person ( name, age, gender ) { this.name = name; this.age = age; this.gender = gender; } // 需要提供一個 Student 的構造函數(shù)創(chuàng)建學生對象 // 學生也應該有 name, age, gender, 同時還需要有 course 課程 function Student ( name, age, gender, course ) { Person.call( this, name, age, gender ); this.course = course; }
在《JavaScript高級程序設計(第三版)》中詳細介紹了繼承的6種方式
七、函數(shù)的四種調用模式 7.1 函數(shù)模式就是一個簡單的函數(shù)調用。函數(shù)名的前面沒有任何引導內容。
function foo () {} var func = function () {}; ... foo(); func(); (function () {} )();
this 的含義:在函數(shù)中 this 表示全局對象,在瀏覽器中式 window
7.2 方法模式方法一定式依附與一個對象,將函數(shù)賦值給對象的一個屬性,那么就成為了方法。
function f() { this.method = function () {}; } var o = { method: function () {} }
this 的含義:這個依附的對象
7.3 構造器調用模式創(chuàng)建對象的時候構造函數(shù)做了什么?由于構造函數(shù)只是給 this 添加成員,沒有做其他事情。而方法也可以完成這個操作,就是 this 而言,構造函數(shù)與方法沒有本質的區(qū)別。
特征:
使用 new 關鍵字,來引導構造函數(shù)。
構造函數(shù)中的 this 與方法中的一樣,表示對象,但是構造函數(shù)中的對象是剛剛創(chuàng)建出來的對象
構造函數(shù)中不需要 return ,就會默認的 return this。
如果手動添加return ,就相當于 return this
如果手動的添加 return 基本類型,無效,還是保留原來 返回 this
如果手動添加的 return null,或 return undefined ,無效
如果手動添加 return 對象類型,那么原來創(chuàng)建的 this 就會被丟掉,返回的是 return 后面的對象
7.4 上下文調用模式上下文就是環(huán)境。就是自己定義設置 this 的含義。
語法
函數(shù)名.apply( 對象, [ 參數(shù) ] );
函數(shù)名.call( 對象, 參數(shù) );
描述
函數(shù)名就是表示函數(shù)本身,使用函數(shù)進行調用的時候默認 this 是全局變量
函數(shù)名也可以是方法提供,使用方法調用的時候,this 是指向當前對象
使用 apply 進行調用后,無論是函數(shù)還是方法都無效了,我們的 this ,由 apply 的第一個參數(shù)決定
參數(shù)問題
無論是 call 還是 apply 在沒有后面的參數(shù)的情況下(函數(shù)無參數(shù),方法五參數(shù))是完全一致的
function foo(){ console.log( this ); } foo.apply( obj ); foo.call( obj );
第一個參數(shù)的使用也是有規(guī)則的:
如果傳入的是一個對象,那么就相當于設置該函數(shù)中的 this 為參數(shù)
如果不傳入?yún)?shù),或傳入 null、undefined 等,那么相當于 this 默認為 window
foo(); foo.apply(); foo.apply( null );
如果傳入的是基本類型,那么 this 就是基本類型對應的包裝類型的引用
在使用上下文調用的時候,原函數(shù)(方法)可能會帶有參數(shù),那么這個參數(shù)再上下文調用中使用 第二個(第 n 個)參數(shù)來表示
function foo( num ) { console.log( num ); } foo.apply( null, [ 123 ] ); // 等價于 foo( 123 );
參考資料
本文原型部分部分引用自《JavaScript原型詳解》,版權歸原作者所有
js閉包的用途
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/91083.html
摘要:中很多特性或者說知識點都是和面向對象編程概念相關的。在多線程中內容有很多,只是簡單說明一下中初步使用多線程需要掌握的知識點,以后有機會單獨再詳細介紹一些高級特性的使用場景。 寫這篇文章的目的是想總結一下自己這么多年來使用java的一些心得體會,主要是和一些java基礎知識點相關的,所以也希望能分享給剛剛入門的Java程序員和打算入Java開發(fā)這個行當?shù)臏市率謧儯M梢越o大家一些經(jīng)...
摘要:在他的重學前端課程中提到到現(xiàn)在為止,前端工程師已經(jīng)成為研發(fā)體系中的重要崗位之一。大部分前端工程師的知識,其實都是來自于實踐和工作中零散的學習。一基礎前端工程師吃飯的家伙,深度廣度一樣都不能差。 開篇 前端開發(fā)是一個非常特殊的行業(yè),它的歷史實際上不是很長,但是知識之繁雜,技術迭代速度之快是其他技術所不能比擬的。 winter在他的《重學前端》課程中提到: 到現(xiàn)在為止,前端工程師已經(jīng)成為研...
摘要:在他的重學前端課程中提到到現(xiàn)在為止,前端工程師已經(jīng)成為研發(fā)體系中的重要崗位之一。大部分前端工程師的知識,其實都是來自于實踐和工作中零散的學習。一基礎前端工程師吃飯的家伙,深度廣度一樣都不能差。開篇 前端開發(fā)是一個非常特殊的行業(yè),它的歷史實際上不是很長,但是知識之繁雜,技術迭代速度之快是其他技術所不能比擬的。 winter在他的《重學前端》課程中提到: 到現(xiàn)在為止,前端工程師已經(jīng)成為研發(fā)體系...
摘要:如果沒有學習過計算機科學的程序員,當我們在處理一些問題時,比較熟悉的數(shù)據(jù)結構就是數(shù)組,數(shù)組無疑是一個很好的選擇。 showImg(https://segmentfault.com/img/bVTSjt?w=400&h=300); 1、常見 CSS 布局方式詳見: 一些常見的 CSS 布局方式梳理,涉及 Flex 布局、Grid 布局、圣杯布局、雙飛翼布局等。http://cherryb...
閱讀 3344·2021-11-10 11:36
閱讀 3244·2021-10-08 10:21
閱讀 2841·2021-09-29 09:35
閱讀 2416·2021-09-22 16:06
閱讀 3959·2021-09-09 09:33
閱讀 1327·2019-08-30 15:44
閱讀 3171·2019-08-30 10:59
閱讀 2982·2019-08-29 15:32