JavaScript權威指南學習筆記,禁止轉載!
6、函數
函數調用方式:
直接調用:foo();
作為對象的方法調用:obj.method();
new:new Foo();
call/apply/bind:func.call(obj);
函數對象中沒有return語句或者return的是基本類型時,如果有this傳回值為this,
(1)建立函數
~函數聲明:function foo(a,b){……}
~函數表達式:
1)var add= function(a,b){……};
2)立即執行函數表達式(function(){……})();、!function(){……} ();等
3)将函數作為傳回值return function(){……};
4)命名式函數表達式var add= function foo(){……};
如,var func=function nfe(){};
alert(func===nfe); //ie6-8傳回false,ie9報錯 nfe is not defined
var func=function nfe(){ nfe(); }; //遞歸調用。如果沒有nfe還是可以用func來遞歸調用,是以不常用。
~Function構造器:var func=Function(‘a’,’b’,’console.log(a+b);’);//不常見
差別:函數聲明會被前置,在定義該函數的作用域通過函數名調用;
函數表達式和函數構造器允許匿名,立即調用;
函數構造器沒有函數名。
(2)this
全局作用域的this:指向window
一般函數的this:在浏覽器裡指向window,在node.js裡指向global object
作為對象方法的函數的this:指向該對象
對象原型鍊上的this:對象原型鍊上的方法的函數的this指向該對象
對象get/set方法的函數的this:指向該對象
~~new與this:
function MyClass(){this.a=37; } //this指向一個空對象,該對象的原型是MyClass.prototype;
var o=new MyClass(); //this會作為MyClass函數的傳回值賦給o;
o; //MyClass {a: 37}
o.a; //37
~~call/apply方法與this:
函數名. call(對象,arg1,arg2,argN)
函數名. apply(對象,[arg1,arg2,argN])
使函數裡的this指向這個對象,并把之後的值指派給函數。差別在于傳參形式不一樣。
如,function add(b,c){return this.a+b+c;} var o={a:1}; add.call(o,2,3);
//6
function add(b,c){return this.a+b+c;} var o={a:1}; add.apply(o,[4,5]);
//10
如,function foo(x,y){console.log(x,y,this);}
foo.call(100,1,2);
//1 2 Number {[[PrimitiveValue]]: 100} (如果第一個參數不是對象,會被轉換為對象類型)
foo.call(null);
//undefined undefined Window
~~bind方法與this(ES5提供的方法,ie9+才支援)
函數名. bind(對象)使函數裡的this指向這個對象。
~~bind與new與this
如function foo(){this.a=100;return this.b;}
var func=foo.bind({b:1});
func; //function foo(){this.a=100;return this.b;}
func(); //1
new func(); //foo {a: 100} (new調用函數時,this依舊指向一個空對象,該對象的原型是func.prototype,不會被bind所改變)
(3)arguments實參
foo.name:函數名,foo.length:函數形參的個數, arguments.length:實參的個數,
arguments.callee是一個指針,指向擁有這個arguments的函數。
~~當參數衆多時,可使用bind與currying:
如,function add(a,b,c){return a+b+c;}
var fun=add.bind(undefined,100);
fun(1,2); //103(this指向無所謂,undefined/null都可以)
var fun2=fun.bind(undefined,200);
fun2(1); //301
?(未看) bind方法模拟
(4)閉包
問題:垃圾回收機制?性能優化:循環引用?
如,function outer(){var a=10; return function(){return a;} }
var func=outer();// function(){return a;} 作為傳回值賦給了全局變量,是以a會一直在記憶體中,也可以在outer函數外部通路它内部的局部變量a了。
func(); //10
//javascript中函數内部可以讀取全局變量,而函數外部無法讀取函數内部的局部變量。
如何從函數外部讀取函數内部的局部變量?在函數内部再定義一個函數。
優點:封裝
缺點:父函數内的局部變量無法釋放,空間浪費,記憶體消耗
把一個函數當做對象去傳遞作為傳回值,類似有這樣特性的語言都有閉包的概念。
(5)作用域
全局作用域
函數作用域
eval作用域,如eval(“var a=1;”)
ES6開始才有塊級作用域
用途:利用函數作用域封裝。
?(未看) ES3執行上下文
7、OOP(面向對象程式設計)
問題:原型鍊上的Object.prototype有哪些屬性方法?
答:valueOf、toString、hasOwnProperty,
對象是類的執行個體,對象作為程式的基本單元,
特性:繼承、封裝、多态、抽象
(1)prototype
如,function Student(){……}
var bosn=new Student();
// Student.prototype是函數對象Student上預設的對象屬性,它的作用是當使用new來構造Student的執行個體bosn時,Student.prototype會作為bosn的原型。
// Student.prototype.constructor指向Student本身(constructor:構造函數的意思)
//增加或删除Student.prototype上的屬性會影響它所建立的執行個體bosn;
如果直接修改Student.prototype不會影響修改之前已經建立的執行個體bosn,
但會影響之後新建立的執行個體。
(2)instanceof
對象 instanceof 函數構造器:
判斷函數構造器.prototype是否出現在對象的原型鍊上。右側如果不是函數對象,會報錯;左邊如果不是對象而是基本類型時,會傳回false,
如,new Object() instanceof Array;
//false 因為new Object()——>Object.prototype——>null,是以左側對象的原型鍊上沒有Array.prototype
(3)實作繼承的方式
function Student(){……}
function Person(){……}
Student.prototype=new Person(); //不推薦
Student.prototype=Object.create(Person.prototype); //推薦(但ES5之後才支援)
(4)模拟重載
例,function Person(){
var args=arguments;//将實參賦給變量
if(typeof args[0]===’object’&& args[0]) {
//&& args[0] 是為了過濾null的參數(因為typeof args[0]也是object)
if(args[0]){
this.name= args[0].name; }
if(args[1]){
this.age= args[1].name; }
}
else{
if(args[0]){
this.name= args[0]; }
if(args[1]){
this.age= args[1]; }
}
}
var bosn=new Person(‘bosn’,27);
var bosn2=new Person( {name:‘bosn2’,age:27} );
(5)鍊式調用
如,function addclass(){}
addclass.prototype.add=function(str){
console.log(str+'is added');
return this;
}//作為對象方法的函數的this指向該對象addclass.prototype
var myman=new addclass(); // myman的原型指向addclass.prototype
myman.add('A').add('B').add('C');
// Ais added
//Bis added
//Cis added
?(未看)9-1 抽象類
(6)例:探測器
子類,基類,detect(),analyze()目前的環境,實作兩種探測器container和link
!function(global){
// DetectorBase基類
function DetectorBase(configs){
if(!this intanceof DetectorBase){
throw new Error(‘Do not invoke without new’); } //必須使用new調用DetectorBase,this指向該對象,該對象的原型指向DetectorBase.prptotype,是以this intanceof DetectorBase為true
this.configs= configs;
this.analyze();
}
DetectorBase.prototype.detect=function(){throw new Error(‘Not implemented’)};
DetectorBase.prototype.analyze=function(){ …… };
//具體類LinkDetector
function LinkDetector(links){
if(!this intanceof LinkDetector){
throw new Error(‘Do not invoke without new’); }//必須使用new調用
this.links=links;
DetectorBase.apply(this,arguments); //調用基類
}
// 具體類ContainerDetector
function ContainerDetector(containers){
if(!this intanceof ContainerDetector){
throw new Error(‘Do not invoke without new’); }//必須使用new調用
this.containers=containers;
DetectorBase.apply(this,arguments); //調用基類
}
//先繼承
function inherit(subClass, superClass){
subClass.prototype =Object.create(superClass. prototype) ;
subClass.prototype.constructor= subClass;
}
inherit(LinkDetector, DetectorBase);
inherit(ContainerDetector, DetectorBase);
//再擴充,防止覆寫
LinkDetector. prototype. detect=function(){ …… };
ContainerDetector. prototype. detect=function(){ …… };
//當機(使得extensible、configurable、writable:false,ES5方法)
Object.freeze(DetectorBase );
Object.freeze(DetectorBase. prototype );
Object.freeze(LinkDetector);
Object.freeze(LinkDetector. prototype );
Object.freeze(ContainerDetector);
Object.freeze(ContainerDetector. prototype );
//暴露到全局對象(在全局對象上添加了LinkDetector等三個屬性,并且enumerable、configurable、writable:false)
Object.defineProperties(global,{
LinkDetector:{value: LinkDetector },
ContainerDetector:{value: ContainerDetector },
DetectorBase:{value: DetectorBase }
});
}(this); //立即執行函數至此結束,好長啊
原型鍊:LinkDetector——> LinkDetector. prototype(detect方法)——> DetectorBase. prototype(detect、analyze方法)
//調用
var cd=new ContainerDetector(‘abcdefg’);
var ld=new LinkDetector(‘http://www.imooc.com/’);
cd. detect();
ld. Detect();
//探測器終于結束了
轉載于:https://www.cnblogs.com/xuxuemin/p/6888803.html