面向對象編程OOP object oriented object
對象:
實質上的事物、可以是任何的東西 包含屬性和方法
類
把部分對象的的共同特征的抽象
封裝
封裝的內容一般有:1、相關的數據 2、基於這些數據的方法
封裝的結果就是暴露給其他對象一個操作這些數據的方法
聚合
將一個復雜的對象分解為多個簡單的對象
繼承
優雅的實現代碼的重用,但是js中沒有具體的類,所以有繼承關系的隻能是對象之間
多態
worker繼承瞭person的全部的方法,這就意味這在這個類中實現瞭不同的talk()方法,當各自類生成的對象調用這個talk方法的時候,會實現各自的功能,這個現象就是多態。
js的基本5大數據類型
數字、字符串、佈爾值、undefined、null
undefined和null的區別
js試圖訪問某個不存在的或者是沒有經過賦值的變量,就會得到一個undefined
1+undefined undefined
1+null 1
函數
什麼是函數:代碼的一種分組形式。
參數:傳遞給函數多餘的參數,函數會自動省略掉沒有用的參數,可以利用函數的滋生的屬性arguments這個數組來訪問參數
預定義函數
js中已經預定瞭一些函數,parseInt()、parseFloat()、idNaN()。。。。。
isNaN() 判斷一個值是否是可以參與運算的數字
encodeURI
eval()
變量作用域
js中不能為變量定義特定的塊級作用域,但是可以為其定制特定的函數域
如果變量是定義在某個函數中,在函數之外這個變量是不可見的
如水果是定義在if或者for語句中,這個變量在外界是可見的
匿名函數
匿名函數的作用:
1、可以將匿名函數作為參數傳遞給其他的函數,接收方就能利用我們所傳遞的函數來完成某些事情
2、定義某個匿名函數來完成某些一次性的事情
回調函數
將函數作為參數傳遞給另一個函數,可以理解為一個函數調用操作交給瞭另一個函數
自調函數
在一個函數後邊加一個()就會自己調用一邊
內部函數
想要函數A裡邊有一個函數B當調用函數A的時候B也會被調用,必須在函數A定義的時候B能夠被調用。
函數和函數對象的區別
var aa=new function (){this.a="fun"}; var cc=function (){this.a="ccFun"}; console.log(typeof cc); console.log(typeof aa);
aa是一個函數 cc是一個函數對象 call方法切換的對象的上下文。
call方法和apply方法
call方法(obj,arg1,arg2)
這個方法可以代替另一對象obj調用一個方法。 將一個函數的對象上下文從初始的上下文改變為this.obj的上下文,
var aa=new function (){this.a="fun"}; function bb(x) { var a="myfun"; console.log(this.a); console.log(x); } bb.call(aa,3); //fun 3
調用bb的時候函數的上下文被切換到aa中,所以a被賦值為fun
apply方法(obj,[arguments])和call的功能一樣但是傳遞的參數是不一樣的
var aa={ a:"12121" }; function bb(x) { var a="myfun"; console.log(this.a); console.log(x); } bb.apply(aa,[3]); //12121 3
調用call和apply以後將obj的所有的屬性和方法都給瞭調用的這個
var base={ member:'qq', xx:function(){ console.log(111); } } function extend() { console.log(this.member); console.log(this.xx); } extend.call(base);
上面的程序和下面的程序的運行效果一致
function base() { this.member='qq'; this.xx=function(){ console.log(111); } } function extend() { base.call(this); //extend會繼承base所有的屬性和方法 console.log(this.member); console.log(this.xx); } extend();
閉包
通過閉包可以突破js的函數作用域鏈:函數內部定義一個函數返回一個函數內部的變量 外部就可以訪問這個變量
var n; var aa=function (){ var b="cc"; n=function() { return b; } } aa(); /aa必須先執行一遍 console.log(n()); //將b升級成瞭全局的變量,突破瞭js的作用域鏈
一個函數需要在其父級函數之後留住對父級作用域鏈的話,此時就是用閉包。。
循環中的閉包
var aa=function (){ var a=[]; var i; for(i=0;i<3;i++){ a[i]=function () { return i; } } return a; } var xx=aa(); console.log(xx[0]()); console.log(xx[1]()); console.log(xx[2]());
沒有輸出對應的1 2 3
下面的這個程序是正確的
var aa=function (){ var a=[]; var i; for(i=0;i<3;i++){ a[i]=(function (x) { return function(){ return x; }; })(i) } return a; } var xx=aa(); console.log(xx[0]()); console.log(xx[1]()); console.log(xx[2]());
會輸出 0, 1 ,2
定義一個將變量本地化的函數MakeLocal()
function makeLocal(x){ return function (){ return x; } } var aa=function (){ var a=[]; var i; for(i=0;i<3;i++){ a[i]=makeLocal(i) } return a; } var xx=aa(); console.log(xx[0]()); console.log(xx[1]()); console.log(xx[2]());
依然會輸出0,1,2
設置secret變量的set和get的操作,這樣secret變量就不可被直接訪問
var getValue,setValue; (function(){ var secret=0; getValue=function (){ return secret; }; setValue=function (v){ secret=v; } })(); setValue(3); console.log(getValue()); //3
練習:
1、編寫一個將16進制的字符串轉換為顏色 將0000FF轉換為0,0,255
function ge(value1,value2){ var result=Math.pow(16,0)*value1+Math.pow(16,1)*value2; return result; } function getRGB(jinzhi){ var zu=[]; for(var i=0;i對象
構造函數,通過constructor能夠得到構造器屬性
function A(name){ this.name=name; } var aa=new A('juan'); console.log(aa.constructor) //得到function A這個函數instanceof操作符
判斷某個對象是不是某個類的實例function A(name){ this.name=name; } var aa=new A('juan'); console.log(aa instanceof A);傳遞對象
當我們拷貝某個對象或者將它傳遞給一個函數的時候,其實就是傳遞這個對象的引用,對這個對象的操作都會實質性的改變這個對象。
對象之間的比較
當我們對對象進行比較的時候,當且僅當兩個引用指向同一個對象時為true;var A={name:1}; var B={name:1}; console.log(A==B) //false兩個對象的引用不同,所以輸出為false
內置對象
數據封裝類對象 Object Array Boolean Number String
工具類對象 Math Date RegExp
錯誤類對象
Object
所有對象的父級對象
屬性 constructor 返回構造屬性 所有的構造屬性都是隻讀的,不能被改變
方法 1、tostring() 2、valueOf()var A=[1,3,4] console.log(A.valueOf()) //[1, 3, 4] console.log(A.toString()) //1,3,4Array
屬性 length
方法 sort ,reverse,join,slice,push,splice
Function()
屬性
length 參數的個數
caller 會返回一個調用該函數對象的外層函數的引用function A(){ console.log(A.caller) ; } function B(){ return A(); } B(); //function B()在B中執行A的結果就是返回A的caller(也就是B)
原型
每一個對象都有自己的原型
function A(){ console.log(1); } console.log(A.prototype) //object通過函數的prototype屬性來為函數添加屬性和方法
function A(name){ this.name=name; this.say=function () { console.log(this.name); } } var aa=new A('juanjuan'); aa.say(); A.prototype.www=function () { console.log(111) } var bb=new A('juanjuan'); bb.www();但是隻限於添加,並不能改變原來的function中的方法,想要改變原來的對象的方法,可以先將原來對象的方法刪除。再在原型上添加方法,這樣就實現瞭改變原來對象方法的功能
var yuan=function (){ this.say=function (x) { console.log(x); } } yuan.prototype.say=function (x) { console.log(x+2) } var yuan1=new yuan(); yuan1.say(2); yuan.prototype.say1=function (x) { console.log(x+2) } yuan1.say1(2);而且原型可以隨時被修改,並且與之相關的對象都會隨之改變。
原型隻能向對象添加方法,但是不能改變原來的方法。這是因為如果遇上對的自身屬性與原型屬性同名的時候 自身屬性的優先級高於原型屬性 如果刪除瞭自身的同名屬性,原型上的這個同名屬性就會出現var yuan=function (){ this.say=function (x) { console.log(x); } } var yuan1=new yuan(); delete yuan1.say; yuan.prototype.say=function (x) { console.log(x+2) } yuan1.say(2);自身屬性和原型屬性
通過hasOwnProperty來判斷var yuan=function (name,age){ this.name=name; this.age=age; } yuan.prototype={ price:100, rating:3 } var yuan1=new yuan("趙文娟",12); for(var cc in yuan1){ console.log(cc+yuan1[cc]); //誰輸出自身和原型的所有的屬性 } console.log(yuan1.hasOwnProperty("name")); //true 自身有著個屬性 console.log(yuan1.hasOwnProperty("price")); //false 自身沒有這個屬性當前對象是否是另一個對象的原型
isPrototypeOf()函數var monkey=function (name,age){ this.name=name; this.age=age; } var human=function(){} human.prototype=monkey; var hh=new human(); console.log(monkey.isPrototypeOf(hh)); //monkey是人類這個對象的原型神秘的proto連接
我們知道當訪問一個在當前對象中不存在屬性的時候,原型就會被納入查找的范圍。這個屬性就可以指向它的原型,var monkey=function (){ this.feeds='香蕉'; this.breaths='空氣'; } var human=function(){} var mon=new monkey(); human.prototype=mon; var hh=new human(); hh.name="juanjuan"; console.log(hh.feeds); //香蕉 console.log(mon.isPrototypeOf(hh)); //true console.log(hh.__proto__); //monkey {feeds: "香蕉", breaths: "空氣"} console.log(hh.prototype); //undefined繼承
js中每一個函數都有一個名為prototype的對象的屬性 new的時候會創建出一個一個對象,並且通過proto指針來連接原型,這樣就形成瞭原型鏈
通過原型鏈實現js 中的繼承function shape(){ this.name='shape'; this.toString=function (){ return this.name; } } function TwoDshape(){ this.name='2Dshape' } function Trigle(side,height){ this.name='Trigle'; this.side=side; this.height=height; this.getArea=function(){ return this.side*this.height/2; } } //對對象的prototype屬性重寫 TwoDshape.prototype=new shape(); Trigle.prototype=new TwoDshape(); //對對象的prototype屬性重寫時,可能會對對象的constructor屬性產生一個負面的影響所以 TwoDshape.prototype.constructor=TwoDshape; Trigle.prototype.constructor=Trigle; var tt=new Trigle(3,4); console.log(tt.toString()); //Trigle console.log(tt.getArea()) //6演示繼承的效果 一定要註意的是對prototype完全覆蓋以後,會對constructor有影響,所以需要
TwoDshape.prototype.constructor=TwoDshape; Trigle.prototype.constructor=Trigle;以上的實驗結果的分析:
遍歷tt對象中的所有屬性。沒有找到一個叫做toString 的方法。就查看proto所連接的實體,也就是回去TwoDshape的new出來的實體中去找,也沒有找到,就會去shape的new的實體找toString這個方法 。同時我們也在原型覆蓋之後重新定義瞭constructor這個屬性,所以TwoDshape和Trigle都是本身console.log(tt instanceof shape); //true console.log(tt instanceof TwoDshape); //true console.log(shape.prototype.isPrototypeOf(tt)) //true將需要共享的屬性都遷移到原型中
當使用構造創建的屬性,每一次的new都會在實體中的多一個這樣的屬性。所以將共享的屬性和方法遷移到原型中是有必要的。function shape(){} shape.prototype={ name:'shape', toString:function (){ return this.name; } } function TwoDshape(){ } TwoDshape.prototype=new shape(); TwoDshape.prototype.constructor=TwoDshape; function Trigle(side,height){ this.side=side; //因為這個屬性是每一個實體獨有的屬性 this.height=height; //因為這個屬性是每一個實體獨有的屬性 } //對對象的prototype屬性重寫 Trigle.prototype=new TwoDshape(); //對對象的prototype屬性重寫時,可能會對對象的constructor屬性產生一個負面的影響所以 Trigle.prototype.constructor=Trigle; Trigle.prototype.getArea=function(){ return this.side*this.height/2 } var tt=new Trigle(3,4); console.log(tt.toString()); //Trigle console.log(tt.getArea()) //6 console.log(tt instanceof shape); //true console.log(tt instanceof TwoDshape); //true console.log(tt.hasOwnProperty('name')) //false console.log(tt.hasOwnProperty('height')) //true值得註意的是擴展原型對象之前,一定要完成繼承關系的構建
隻繼承原型
盡可能的將重用的屬性和方法添加到原型中,這樣就通過原型鏈就實現瞭繼承,不再需要shape的實體來實現繼承關系。function shape(){} shape.prototype={ name:'shape', toString:function (){ return this.name; } } function TwoDshape(){ } TwoDshape.prototype=shape.prototype; TwoDshape.prototype.constructor=TwoDshape; function Trigle(side,height){ this.side=side; //因為這個屬性是每一個實體獨有的屬性 this.height=height; //因為這個屬性是每一個實體獨有的屬性 } //對對象的prototype屬性重寫 Trigle.prototype=TwoDshape.prototype; //對對象的prototype屬性重寫時,可能會對對象的constructor屬性產生一個負面的影響所以 Trigle.prototype.constructor=Trigle; Trigle.prototype.getArea=function(){ return this.side*this.height/2 } var tt=new Trigle(3,4); console.log(tt.toString()); //Trigle console.log(tt.getArea()) //6 console.log(tt instanceof shape); //true console.log(tt instanceof TwoDshape); //true console.log(tt.hasOwnProperty('name')) //false console.log(tt.hasOwnProperty('height')) //true測試的結果和上面的一致,但是js在找toString的時候,直接找的是原型
子對象訪問父對象
js中本身沒有這樣的屬性,但是可以通過子類調用父類的同名方法來實現function shape(){} shape.prototype={ name:'shape', toString:function (){ var result=[]; if(this.constructor.urber){ result[result.length]=this.constructor.urber.toString(); } result[result.length]=this.name; return result.join(',')+"11"; } } function TwoDshape(){ } function F(){}; F.prototype=shape.prototype; TwoDshape.prototype=new F(); TwoDshape.prototype.constructor=TwoDshape; TwoDshape.urber=shape.prototype; TwoDshape.prototype.name='2D圖形'; function Trigle(side,height){ this.side=side; //因為這個屬性是每一個實體獨有的屬性 this.height=height; //因為這個屬性是每一個實體獨有的屬性 } //對對象的prototype屬性重寫 var F=function(){}; F.prototype=TwoDshape.prototype; Trigle.prototype=new F(); //對對象的prototype屬性重寫時,可能會對對象的constructor屬性產生一個負面的影響所以 Trigle.prototype.constructor=Trigle; Trigle.urber=TwoDshape.prototype; Trigle.prototype.getArea=function(){ return this.side*this.height/2 } Trigle.prototype.name="三角形"; var tt=new Trigle(3,4); console.log(tt.toString()); //shape11,2D圖形11,三角形11改進瞭toString 用urber屬性指向瞭父類的原型
將繼承的部分封裝function extend(child,parent) { var F=function () {}; F.prototype=parent.prototype; child.prototype=new F(); child.prototype.constructor=child; child.uber=parent.prototype; //實現子類繼承父類的屬性 } function shape(){} shape.prototype={ name:'shape', toString:function (){ var result=[]; if(this.constructor.uber){ result[result.length]=this.constructor.uber.toString(); } result[result.length]=this.name; return result.join(',')+"11"; } } function TwoDshape(){} extend(TwoDshape,shape); TwoDshape.prototype.name='2D圖形'; function Trigle(side,height){ this.side=side; //因為這個屬性是每一個實體獨有的屬性 this.height=height; //因為這個屬性是每一個實體獨有的屬性 } extend(Trigle,TwoDshape); Trigle.prototype.getArea=function(){ return this.side*this.height/2 } Trigle.prototype.name="三角形"; var tt=new Trigle(3,4); console.log(tt.toString()); //shape11,2D圖形11,三角形11對象之間的繼承 淺拷貝
js中沒有類 丟掉構造函數來實現繼承 就是多個屬性的拷貝
使用多個屬性的拷貝實現瞭類之間的繼承,中間沒有使用函數,js就實現瞭類之間的繼承function extendCopy(p) { var c={}; for(var i in p){ c[i]=p[i]; } c.uber=p; return c; } var shape={ name:'圖形', toString:function (){ return this.name; } } var twoDee=extendCopy(shape); twoDee.name='2D圖形'; twoDee.toString=function(){ return this.uber.toString()+this.name; } var trigle=extendCopy(twoDee); trigle.name="三角形"; trigle.toString=function(){ return this.uber.toString()+this.name; } trigle.side=3; trigle.height=4; trigle.getArea=function(){ return this.height*this.side/2 } console.log(trigle.toString()); //圖形2D圖形三角形 console.log(trigle.getArea()); //6深拷貝
之前的extendCopy實現 的是淺拷貝,要想實現深拷貝實際上遇到一個對象引用性的屬性的時候,我們需要再次調用深拷貝函數
實現深拷貝function deepCopy(p,c) { var c=c||{}; for(var i in p){ if(typeof p[i]=="object"){ c[i]=(typeof p[i===Array])?[]:{}; deepCopy(p[i],c[i]) //實際上是一個遞歸實現的深拷貝 } else{ c[i]=p[i]; } } return c; }比較深拷貝和淺拷貝
function deepCopy(p,c) { var c=c||{}; for(var i in p){ if(typeof p[i]=="object"){ c[i]=(typeof p[i===Array])?[]:{}; deepCopy(p[i],c[i]) //實際上是一個遞歸實現的深拷貝 } else{ c[i]=p[i]; } } return c; } function extendCopy(p) { var c={}; for(var i in p){ c[i]=p[i]; } c.uber=p; return c; } var p={ numbers:[1,2,3], letters:['a','b','c'], obj:{ name:'zhaowenjuan' }, boolean:true } var test=deepCopy(p); test.numbers.push(3,4); console.log(test.numbers); //1,2,3,4 console.log(p.numbers); //1,2,3 發生的是深拷貝 備份與原來相互獨立 var test2=extendCopy(p); test2.numbers.push(3,4); console.log(test2.numbers); //1,2,3,4 console.log(p.numbers); //1,2,3,4 發生的是淺拷貝 備份與原來是一個