javascript fundamental concept

http://hzjavaeyer.group.iteye.com/group/wiki?category_id=283

上面這個url很是適合javascript初學者閱讀,感謝原做者的分享javascript

什麼是script?

A script is a series of instructions that a computer can follow to achieve a goal.html

A browser may use different parts of the script depending on how the user interacts with the web page.java

Scripts can run different sections of the code in response to the situation around them.jquery

相似於菜譜,汽車維修操做說明書。c++

 

javascript中的類型 

js分爲兩類:原始類型和(值引用)和Object類型(引用類型)。在瀏覽器宿主環境下,JS又能夠分類爲:原生對象,宿主對象,瀏覽器擴展對象(好比ActiveXObject).原始類型和對象類型他們的區別在於原始類型是值引用,分配在棧空間中,Object類型則在棧中分配的只是變量自己,它指向一個分配在堆中的內存塊。web

 

數據類型隱式轉換:

  • 當數字和字符串執行元算時," +  " 操做將會把數字隱藏轉換爲字符串, " - "或者*/操做將會把字符串轉換爲數字

 

  • 注意看下面的代碼:3.1415這個數字若是放在()中而且使用「 . 」點操做符時,瀏覽器默認行爲是將他轉換爲Number對象類型,而該Number對象類型是有一個toFixed()方法的;

(3.1415).toFixed(2)express

"3.14"編程

console.dir(new Number(3.1415))數組

VM278:2 Number瀏覽器

一樣的

"hello world".split(" ");
["hello", "world"]

上面這段代碼執行時,瀏覽器將值直接量轉換爲String對象類型,故而能夠調用String的split方法

  • if(表達式)這裏表達式也將隱式轉換爲布爾值;
  • == 將隱式轉==兩邊的值

顯式類型轉換方法

Number(), String(),Boolean(),parseInt(),parseFloat(), !, !! 

typeof與instanceof的區別

  • typeof operator returns a string indicating the type of the unevaluated operand.

typeof操做符能識別除了Null以外的標準原始類型,不能識別具體的對象類型(除了Function外)typeof(null)返回object

返回如下內容「 undefined, boolean, number, string, symbol, function, object"

typeof operand
  • instanceof operator tests whether an object has in its prototype chain the prototype property of a constructor

instanceof操做符能夠識別全部的內置對象和自定義對象,可是不能識別原生初始類型的值

返回true或者false

object instanceof constructor
  •  Object.prototype.toString.call 能夠識別除了自定義類型對象外的全部類型:
Object.prototype.toString.call(123);
"[object Number]" 
Object.prototype.toString.call("this is a string");
"[object String]"
  • constructor方法來識別類型

使用.constructor能夠識別全部的類型(除了null,undefined外,固然即便是null,undefined,使用.constructor也能夠返回他們自己!!)。其原理爲:當傳入原始類型時,點操做符使得js自動轉換爲對應的對象類型,built-in和自定義對象則會返回對象的構造函數自己。

function getConstructorName(obj){ return obj && obj.constructor && obj.constructor.toString().match(/function\s*([^(]*)/)[1]; }
undefined
getConstructorName(null)
null
getConstructorName(undefined)
undefined
getConstructorName([])
"Array"
getConstructorName({})
"Object"
getConstructorName(123)
"Number"
getConstructorName("string")
"String"

 constructor詳解

 上面經過使用obj.constructor.toString方法可以識別全部自定義對象的前提是:你需要預先執行下面的一行代碼constructoFunction.prototype.constructor = constructorFunction;

當設置構造函數的prototype原型對象屬性時,不能僅僅經過一條constructorFunction.prototype = { // 一個literal對象};這種方式來定義產出對象的原型,由於這麼作的話,意味着constructorFunction.prototype.constructor = Object(由於obj.constructor永遠指向建立本身的構造函數自己,而在這裏constructorFunction.prototype對象其實是由new Object來創造的,因此constructorFunction.prototype.constructor = Object!!)。

解決或避免上面問題的最佳實踐是:每次設置構造函數的prototype屬性時,順帶就執行constructorFunction.prototype.constructor = constructorFunction;

下面來解釋一下相關背景。

1.每個對象都有一個constructor屬性包括構造函數自己,由於構造函數也是對象;

2.任何對象的constructor始終指向建立該對象的構造函數(只要檢視等效的new XXXX,就知道對象的構造函數)

3.每個函數都有一個默認的屬性prototype,而這個prototype屬性也是一個對象,他的constructor默認指向這個函數;

4.任何函數的prototype屬性對象的constructor默認就是函數自己(在不顯示設置prototype的狀況下)

5.obj.constructor = objConstructor.prototype.constructor這是定律,因爲prototype.constructor或顯式或隱式被設置爲構造函數自己,固然也能夠設置爲其餘的函數

看看下面的代碼:

function Person(name) {   
    this.name = name;   
}   
var p = new Person("ZhangSan");   
console.log(p.constructor === Person);  // true   p的構造函數爲Person,由於p是經過new Person來建立的
console.log(Person.prototype.constructor === Person); // true   任何函數的prototype屬性對象的constructor默認就是函數自己。
console.log(p.constructor.prototype.constructor === Person); // true

上面是在未設置Person的prototype屬性對象的狀況下默認的new Person行爲;下面咱們增長一行代碼,即:指定Person構造函數的prototype屬性,增長一個getName廣泛方法:

function Person(name) {   
    this.name = name;   
}   
Person.prototype = {  //設置Person的prototype爲一個literal對象,供後代繼承
  getName: function() {
    return this.name;
  }
} 
var p = new Person("ZhangSan");   //因爲任何對象的constructor指向建立本身的構造函數(new XXXX中的XXXX),實際由construcfunc.prototype.constructor或顯式或默認來指定
console.log(p.constructor === Person); // false //obj.constructor = objConstructor.prototype.constructor這是定律 ,而這裏Person.prototype的構造函數爲Object, Object的prototype.constructor默認就指向Object構造函數自己,所以p.constructor === Object console.log(Person.prototype.constructor === Person); // false 實際上Person.prototype.constructor === Object console.log(p.constructor.prototype.constructor === Person); // false 

解決上面的問題,只要在設置Person.prototype屬性對象以後,添加一句: Person.prototype.constructor = Person便可!!

 

function:

function自己是一個object,它能夠被賦值給任何變量,也能夠有不少自定義的屬性

immediate invoked function expression(IIFE):  function(){}()

method vs function: 一個method是一個對象的一個函數屬性(a method is a function that is a property of an object)例如:

var console={

  log:function(){}

}

console.log(「log是console的method!");

String methods:

下面的方法或者屬性是string對象定義的方法或者屬性!

length: 這個length其實是一個屬性,而不是一個方法。

indexOf

var line = "HAL: I'm sorry, Dave. I'm afraid I can't do ▶
that";
alert( line.indexOf("I'm") ); // 5
alert( line.indexOf("I'm", 6) ); // 22

slice,substr,substring函數:

var greeting = "Hello, Andrew, what's up?",
name = greeting.slice(7, 13);
alert(name); // Andrew

split:該函數將一個字符串使用空格符做爲分割依據將每一個單詞分開放到一個數組中

var arr = "apples oranges peaches bananas".split(" ");
console.log(arr); // ["apples", "oranges", "peaches", ▶
"bananas"]

toLowerCase, toUpperCase

Date Methods

Date object有不少實用的方法可供使用

getDate,getDay,getFullYear,getHours,getMilliseconds,getMinutes,getMonth,getSeconds,getTime,getTimezoneOffset

setDate,setFullYear,setMinute,setMonth,setSeconds,setTime,setMilliseconds,setHours,

parse:能夠用於講一個字符串解析爲Date object

alert( Date.parse("June 18, 1970") ); // 14529600000
alert( Date.parse("2010/11/11") ); // 1289451600000
var d = new Date(Date.parse("1995/04/25")); // Tue Apr 25 ▶
1995 00:00:00 GMT-0400 (EST)
alert(d);

Array methods

join:這是和split相反的動做,它將把數組中的全部元素結合在一塊兒造成一個string

alert( ["cats", "dogs", "hamsters", "fish"].join(' ') );
// "cats dogs hamsters fish"
alert( "this should not have spaces".split(" ").join("_") );
// "this_should_not_have_spaces"

pop/shift:

var arr = ["cats", "dogs", "hamsters", "fish"];
console.log( arr.pop() ); // "fish"
console.log( arr.shift() ); // "cats"
console.log( arr ); // ["dogs", "hamsters"]

push/unshift:push將在數組的尾部增長一個item,unshift則在數組的開始增長一個item

var arr = ["Beta", "Gamma"];
arr.push("Delta");
console.log(arr); // ["Beta", "Gamma", "Delta"];
arr.unshift("Alpha");
console.log(arr); // ["Alpha", "Beta", "Gamma", "Delta"]

reverse:

alert( ["Crockford", "Resig", "Zakas"].reverse() ); ▶
// "Zakas, Resig, Crockford"

slice:有時,你但願將你的數組slice成多個部分,這個slice就提供這個功能。包含兩個參數:index of the starting element and index of the element after the last one you want to slice.

alert( ["a", "b", "c", "d", "e"].slice(2, 4) ); ▶
// ["c", "d"]
alert( ["f", "g", "h", "i", "j"].slice(1) ); ▶
// ["g", "h", "i", "j"]

sort:按照字母排序:

["d", "e", "c", "a", "b"].sort(); // ["a", "b", "c", "d", ▶
"e"]
[0, 5, 10, 15, 20, 25].sort();//[0, 10, 15, 20, 25, 5].

在上面對數字的排序可能並非你但願獲得的效果,一個可行的方案是sort函數傳入一個函數:

var arr = [5, 2, 3, 4, 1,10,20];
arr.sort(function (a, b) {
return a - b;
});
console.log(arr); // [1, 2, 3, 4, 5 ,10 ,20];

Math functions

javascript也提供了一個Math 對象,你雖然不能像Date對象同樣二次建立對象,可是你卻能夠直接調用Math對象的方法

min,max,random,round,ceil,floor,pow,

alert( Math.min(9, 1, 4, 2) ); // 1
alert( Math.random() ); // 0.5938208589795977 (you'll ▶
probably get something else)
alert( Math.random() * 10 ); // 6.4271276677027345 (again,▶
your mileage may vary)
alert( Math.round(10.4) ); // 10
alert( Math.round(10.5) ); // 11
alert( Math.ceil(10.4) ); // 11
alert( Math.floor(10.5) ); // 10
function getRandomNumberInRange(min, max) {
return Math.floor( Math.random() * (max - min + 1) + ▶
min);
}
alert( getRandomNumberInRange(0, 100) ); // 39; you'll ▶
probably get something different.

This

This是javascript預留的keyword,你能夠將this想象成一個你不能控制的動態變量。this變量的值將隨着你在代碼的哪個地方而不斷變化其值:

默認狀況下,this將指向global對象。this沒有一個合適的名字,可是他卻有一個屬性指向它本身:window.若是你看看下面這個小的代碼,就有些感受了:

var my_variable = "value";
function my_function () { return "another_value" }
alert( this.my_variable ); // "value"
alert( this.my_function() ); // "another_value"
var global = {
window : global
. . .
};
this = global;

這意味着你能夠訪問你的全局變量或者函數做爲window的屬性,而這比用this來訪問這些全局變量函數更加廣泛。這依然有點搞,由於你能夠訪問window對象來使用global對象上的函數或屬性,即便this能夠指向其餘的對象。甚至,你不用使用window.xx的方式調用,除非你有一個local的變量或者函數而覆蓋了全局的函數或變量!

new keyword

第一種改變this指針的方法是使用new 關鍵字:

你可能知道了javascript對象是什麼(屬性和函數的封裝)。將相關的功能封裝到一個合適的object中而且經過合適的接口來訪問是一個很是很是很是基礎的OOP編程模式。Classes就像一個藍圖:他們不是實際的對象,可是他們描述了一旦經過該class建立一個object,那麼這個object應該具有的函數和變量。javascript是面向對象的,可是它卻不用class這個概念。想法,它使用prototypes這個概念。prototypes是一些實際的對象objects,咱們可使用這些對象objects做爲鮮活的建立其餘對象的藍圖」對象「。

在其餘語言中,好比c++,使用一個構造函數來建立新的對象。而在javascript中,構造函數是普通的函數,若是在它前面添加一個new關鍵字則能夠建立新的對象。

好比咱們要建立truck對象,

function Truck(model) {
this.num_of_tires = 4;
this.kilometers = 0;
this.model = model;
}
var my_truck = new Truck("Hercules");
console.log(my_truck);

上面的代碼注意兩點:首先,咱們定義全部的變量做爲this對象的屬性;其次,咱們沒有從這個函數中返回任何東西。這兩點是由於咱們計劃使用new關鍵字來調用這個函數建立對象。注意咱們是如何使用new關鍵字的。它作了兩件事:首先,它更改this指針指向一個新的乾淨的對象。很明顯,咱們而後能夠增長咱們定製的屬性在這個函數中。第二件new乾的事情是:它將this返回。如今my_truck變量將指向新的對象,這個my_truck就像咱們下面的代碼同樣的結果:

var my_truck = {
num_of_tires : 4,
kilometers : 0,
model : "Hercules"
};
console.log(my_truck);

這就是使用new的想法:咱們得到一個建立新對象的超級簡單的方法。很明顯,你若是僅僅須要一個或兩個簡單的對象,你不須要這麼作。一般你在須要有不少功能而且但願打包到一個對象中時,你才須要這種new的模式,或者你但願建立一系列相似的對象時,也可使用new方法來建立對象。

call and apply

第二種更改this指針的方法是call或者apply.在javascript中一切皆爲對象---即使function也是對象。既然function是對象,那麼他們也能夠有屬性和方法(property,method)。call和apply就是每個function對象的兩個默認方法。

這兩個方法存在的價值就是在函數中修改this指針

function Truck (model, num_of_tires) {
this.num_of_tires = num_of_tires;
this.kilometers = 0;
this.model = model;
}
var basic_vehicle = { year : 2011 };
Truck.call(basic_vehicle, "Speedio", 4);
console.log(basic_vehicle);

小知識點:primitive vs reference values:你可能想,雖然咱們更改一個已知的對象,咱們會丟失那些變動,除非咱們將他們賦值給一個新的變量。若是變量是一個primitive value,那麼這將是對的。然而,objects(arrays)是referrnce values:values passed by referrnce.一個primitive value(好比一個string或者number)是在計算機內存中保存的,而咱們使用一個變量來訪問它。當咱們將這個變量傳給一個函數時,咱們會copy那個值到一個新的內存,而將函數參數指向這個新的copy內存,而在函數中對這個copy操做,所以原始的primitive value並不會被變動。(由於內存不一樣),可是當咱們使用reference value時則不一樣:當咱們傳遞一個object或者一個array給一個function時,形式參數將指向原始object/array相同的內存。這意味着,Truck.call(basic_vehicle)中的this和函數外的basic_object中的this是徹底一個basic_object.因此沒有必要作任何assign動做,由於basic_vehicle如今已經有了由Truck賦值的全部屬性。若是咱們將Truck.call(basic_vehicle)的返回值付給一個變量,將會發生什麼,它將會是undefined,由於Truck without new將不會返回任何東西!

在這裏,咱們經過Truck函數的call方法來調用Truck函數。call的第一個參數是咱們但願在函數中this所指向的對象。既然咱們沒有使用new來調用Truck,它將不會建立任何新的對象或者返回this.這也是咱們所但願的,由於咱們僅僅是在修改已經存在的對象,而不是從新建立一個!

在將成爲this所指向的對象參數後(你能夠稱爲函數的上下文context),咱們能夠傳入任何須要的參數。這些將做爲參數被傳入將被執行的函數。上面的例子中,第二個參數"Speedio"將被傳給Truck函數做爲其第一個Parameter。第三個參數6將成爲Truck的第二個參數。

和call向對應,還有一種方法叫作apply.二者的區別是:apply只接受兩個參數。第一個是context object,第二個是將被傳入function的一個參數的數組。若是你有一個做爲數組的參數,這種方法將很是有用。例如,

function Truck (model, num_of_tires) {
this.num_of_tires = num_of_tires;
this.kilometers = 0;
this.model = model;
}
var basic_vehicle = { year : 2011 },
user_input = "Speedio 18";
Truck.apply(basic_vehicle, user_input.split(" "));
console.log(basic_vehicle);

Inside an Object

還有另一種方法能夠更改this的值。

var waitress = {
name : "Ashley",
greet: function (customer) {
customer = customer || " there!";
return "Hi " + customer + " My name is " +this.name + "; what can I get you?";
}
};
alert( waitress.greet("Joe") ); // Hi Joe My name is ▶
Ashley; what can I get you?

 你們都知道使用new關鍵字來調用一個構造函數是一種古老而有用的建立對象的方法,好比下面的構造函數:

function Computer (name, ram_amount, memory_amount) {
this.name = name;
this.RAM = ram_amount; // in GBs
this.space = memory_amount; // in MBs
}

上面是一個普通的構造函數,若是咱們使用new Computer("MacBook", 2, 250000)調用的話,咱們將建立一個Computer對象。注意:Computer並非全部computer對象的prototype(或者blueprint)Computer僅僅是這些computer對象的構造函數constructor,也就是構造產出對象own Property和own Method!!!!.

若是咱們但願給上述computer對象增長一個函數,好比保存文件的能力函數將會發生什麼?你可能這麼作:

function Computer (name, ram_amount, memory_amount) {
this.name = name;
this.RAM = ram_amount;
this.space = memory_amount;
this.files = [];
this.store_file = function (filename, filesize) {
this.files.push(filename);
this.space -= filesize;
};
}

上面的方法看似無傷大雅,可是這裏存在一個問題。你不該該在你的constructor function中建立function,由於每次你建立一個新的對象copy時,你將會建立那個function的新的copy,而這將佔用內存。關鍵是:沒有必要這樣作,由於咱們能夠僅僅保存那個function的一個copy,而該copy將爲全部的Computer object服務。這之因此工做,是由於咱們在構造函數中使用this來引用了對象自己

咱們須要作的是一種方法用於將每一份Computer對象指向一個store_file函數的方法。咱們迄今所討論的方法是有效的,由於function是一個method,因此this指向一個對象。咱們能夠這樣作--而且只須要一個函數--而使用Computer構造函數的prototype屬性:這意味着使用new Computer建立對象時,除了Computer構造函數own property/own method之外,還有一個隱形的__proto__原型鏈生成以便供繼承使用!

Computer.prototype.store_file = function (filename, filesize) {
this.files.push(filename);  //this指的是new Computer的返回對象 this.space -= filesize;
};

記住:function自己也是object哦,因此他們能夠有屬性的,在上面這個代碼片斷中,咱們訪問了Computer構造函數的prototype屬性,而該屬性的值其實是一個object,該對象是全部Computer objects用於inherit的祖。這意味着:當你運行: my_computer.store_file("image.jpg",26)時,js引擎發現my_computer自己並無一個叫作store_file的method,因此js引擎會繼續查看my_computer的prototype object(Computer.prototype來指示)(經過my_computer.__proto__來尋址),若是在prototype中發現這個方法則調用它,若是沒有發現,則繼續向prototype chain中繼續向上搜索,直到最上層Object.prototype---Object是javascript中的全部對象的父親。

在這個使用new+constructor來建立新的對象的案例中,有兩點須要說明:

1. 構造函數自己建立一個對象(denoted by this inside the function);在這個函數中,咱們對該對象的屬性進行賦值操做;

2. 每個使用指定constructor建立的任何一個對象的prototype或者說parent object都保存在ConstructorFunction.prototype(注意構造函數自己也是一個對象)裏。全部prototype/parent對象的屬性或者方法均可以從"child"object中來訪問(機制是由js的prototype chain檢索來實現的)

這就是爲何被稱爲"Prototypal Inheritance";每個從相同的構造函數建立的對象都繼承於相同的prototype。很重要的一點是:若是一個給定的child object有一個它本身的屬性,好比說my_obj.name,那麼prototype不會被檢索該屬性。同時,若是你修改一個child object的屬性或者方法,你只修改了那個對象自己,並不會影響到prototype object。

如下爲例:

function Product(name) {
if (name) {
this.name = name;
}
}
Product.prototype.name = "To be announced"; //提供默認的name,若是new Product時傳入name則覆蓋這個原型上的默認值 
Product.prototype.rating = 3;
var ipad = new Product("iPad");
alert( ipad.name ); // "iPad";
alert( ipad.rating ); // 3

// ipad from above
ipad.rating = 4;
ipad.rating; // 4
Product.prototype.rating; // 3

 

這裏咱們建立了一個簡單的Product對象,你能夠看到name屬性打印的是ipad對象自己修改事後的name,而rating屬性則是來自prototype的繼承(實際上,若是你在child object上修改也會覆蓋掉prototype的屬性)。經過修改rating這個值,咱們實際上就是在ipad對象上添加了一個rating屬性,由於對象的原型對象上的屬性是隻讀的!!,既然ipad對象本身有了rating屬性,當咱們訪問ipad.rating屬性時,js引擎將再也不向上級prototype檢索。然而,即使如此,prototype的rating屬性值也沒有被改變。

原型鏈

this的scope

當一個函數沒有使用" . "來調用該函數時,這個函數中的this指針是和這個函數被調用前面一行的this是指向同一個對象的

好比:

var joe={
 firstName: 'joe',
 lastName: 'will',
fullName: function() {
  return this.firstName + ' ' + this.lastName;
}
}
var fullName = joe.fullName;
var firstName = 'John';
var lastName = 'papa';
console.log(fullName()); //注意因爲fullName()函數被直接調用,未用.方式來引用,所以fullName函數中的this和他前面一行指向是相同的。在這裏也就是global的空間,而全局空間中確實有firstName和lastName,故而輸出」 John papa「

另一個簡單的例子,經過jquery定位到全部的"all" button,點擊之後全部的answer div內容將變爲button的text:

html:
Mark ALL test: <button class="all">Yes</button><button class="all">No</button>
<div class="answer">?</div>
<div class="answer">?</div>
<div class="answer">?</div>

javascript:
$(function(){
 $('.all').click(function(){
//在這個context時,this是指$('.all')即button
   var that = this;
   $('.answer').each(function(){
//這裏this是指各個有.answer類的單個div $(
this).text($(that).text()); }); }); });

 

 

Object Methods

迄今爲止,咱們討論除了Objects外的全部type的方法,之因此這麼安排是由於:

1.我但願保持全部object-related資料在一個集中的地方;

2.大多數你可使用的methods就是你本身建立的那些方法

然而,對於object對象自己也有一些built-in的方法。既然你建立的任何對象都是一個Object(即:從Object.prototype來繼承的),那麼這些方法都是隨時可用的:

hasOwnProperty

function Person(name) {
this.name = name;
}
Person.prototype.legs = 2;
var person = new Person("Joe"), prop;
for (prop in person) {
console.log(prop + ": " + person[prop]);
}
// in console:
// name : Joe
// legs: 2

在這裏,若是你的對象是從有着屬性的prototype繼承過來的,那麼那些屬性自己也會被loop檢索到。這可能會打印出你不但願看到的結果(好比從prototype中繼承出來的leg)。在這種狀況下,對象可使用一個實用的方法hasOwnProperty來決定一個給定的property是否屬於object自己仍是它的prototype.

function Person(name) {
this.name = name;
}
Person.prototype.legs = 2;
var person = new Person("Joe"), prop;
for (prop in person) {
if (person.hasOwnProperty(prop)) {
console.log(prop + ": " + person[prop]);
}
}
// console:
// name: Joe

toString

每個object都有一個toString方法。這個方法在object在任何地方將被打印時調用;它的做用是轉換對象爲一個字符串。不一樣的built-in types將作不一樣的事情:strings明顯不會改變;數字將會成爲數字的字符串;dates將格式化爲date strings。可是通常的object將會怎樣呢?

var o = { name : "Andrew" };
alert( o.toString()); // "[object Object]"

上述代碼實際上毫無用處,只是告訴你它是一個object。如何解決這個問題呢?你能夠定義你本身的toString函數來覆蓋prototype中的這個函數

var person = {
name : "Joe",
age : 30,
occupation: "Web Developer",
toString : function () {
return this.name + " | " + this.occupation;
}
};
alert( person.toString() ); // "Joe | Web Developer"

valueOf:

該函數將返回一個primitive value以便表明你的對象。好比:

var account = {
holder : "Andrew",
balance : 100,
valueOf : function () {
return this.balance;
}
};
alert( account + 10 ); // 110

Object.create:creating object from object

 

var Human = {
arms: 2,
legs: 2,
walk: function() { console.log("Walking"); }
}
<< {"arms": 2, "legs": 2, "walk": function ()
➥{ console.log("Walking"); }}
lois = Object.create(Human);
<< {"arms": 2, "legs": 2, "walk": function ()
➥{ console.log("Walking"); }}

 

Closure

closure是javascript最重要的一個功能之一。若是你熟悉其餘的編程語言(特別是oop的語言,好比c++),你必定知道private variable(私有變量)這個概念。一個私有變量就是一個僅僅被對象的方法可以訪問而不能在對象以外直接訪問的變量。Javascript自己並無這個功能,可是咱們可使用closure來"make"private variables。記住兩點關於貢獻於closure的javascript特性:

1.everyting is an object, even functions.這意味着咱們能夠從一個函數返回一個函數

2.Anonymous,self-invoking functions是沒有名字的當即運行而一般只運行一次的函數

如今咱們使用這兩個概念來建立一個closure。首先看看一般的javascript代碼:

var secretNumber = 1024;
function revealSecret(password) {
if (password === "please") {
return secretNumber++;
}
return 0;
}

上面這段代碼對你來講是很普通的。咱們有一個叫作secretNumber的變量和一個若是密碼正確返回加1的函數。在這裏,secretNumber變量自己沒有任何祕密可言,他是一個任何函數都能訪問的全局變量。爲了增長私密性,爲何咱們不將它放到函數內部呢?這樣咱們從函數外面就將不能訪問這個變量了!

不過注意了,咱們不能那樣作哦,由於若是那樣,每次咱們調用那個函數,js引擎都將reset setcretNumber變量到1024!怎麼作呢?Closure能夠完成上面的場景!

var revealSecret = (function () {
var secretNumber = 1024;
return function (password) {
if (password === "please") {
return secretNumber++;
}
return 0;
};
}());
alert( revealSecret("please") ); // 1024
alert( revealSecret("please") ); // 1025

注意revealSecret自己被賦予一個anonumous self-invoking function的返回值。既然那個匿名函數當即運行,revealSecret就將被賦予該函數的返回值。在這個例子中,它又是一個函數。下面的事實使人印象深入:內部函數(被返回的函數)自己引用了secretNunber變量從它的"parent"function's scope.即便匿名函數已經返回,inner function依然可以訪問到這個變量。那就是closure:an inner function having access to the scope of it's parent function, even after the parent function has returned!!!這種方法,secretNUmber僅僅被初始化一次,它能夠在每次內部函數調用時加1,而任何其餘人不能訪問它,由於它被匿名函數的scope所保護。總的來講:closure是暴露從函數外來訪問一個函數的scope的有限控制(closure is the concept of exposing limited control of a function's scope outside the function.)

這種closure在建立模塊時也很是有用:

var a_module = (function () {
var private_variable = "some value";
function private_function () {
// some code;
}
return {
public_property : "something"
// etc : " ... "
};
}());

這就是module pattern:在一個anonymous selft-invoking function中,咱們建立沒法在匿名函數外訪問的變量和方法。而後,咱們返回一個對象,這個對象包含public屬性和可以訪問匿名函數閉包裏面的私有變量或者方法的公共方法。經過這種方式,那些沒必要暴露的信息被屏蔽,而咱們只須要暴露共有的函數或者變量。

Errors

有兩種錯誤:一種是當你寫代碼時犯的錯誤,好比語法錯誤或者類型錯誤,另外一種是沒法預知的錯誤,好比用戶的輸入錯誤或者missing feature in the js engine.雖然js引擎一般默默地處理曉得錯誤,好比忘記了分號,一些大的嚴重錯誤js會拋出異常。固然,咱們也能夠主動拋出咱們本身的錯誤異常。

try {
// code that might cause an error here
} catch (e) {
// deal with the error here
}

咱們將可能產生錯誤的代碼封裝在try塊中。若是咱們不這樣作,js引擎會處理那個錯誤,這可能意味着中止執行你的代碼而且可能向用戶顯示錯誤:而這可能並非咱們想要的。若是一個錯誤被拋出,error object將被傳遞到catch block。在catch代碼塊中,你能夠處理這個錯誤。

在error對象中,不一樣的web瀏覽器包含不一樣的屬性。他們都會包含error name和error message.Firefox也會包含文件名和文件行。Chrome包含一個stack trace.也有一些其餘的東西,可是主要的就是name和message.

function make_coffee(requested_size) {
var sizes = ["large", "medium", "small"],
coffee;
if (sizes.indexOf(requested_size) < 0) {
throw new Error("We don't offer that size");
}
coffee = { size: required_size, added: ["sugar", ▶
"sugar", "cream" ] };
return coffee;
}
try {
make_coffee("extra large");
} catch (e) {
console.log(e.message);
}

 Events:

什麼是event?

event,簡單地說,就是在DOM中的元素上發生的事件,好比:

1.the page loaded;2.an element was clicked;3.the mouse moved over an element;4.when something like this happnes, you may often want to do something.好比,當page loaded,運行一段js代碼,或者當元素被clicked時,執行這個函數。。。

常見的event事件有:click,mouseover,mouseout,keypress,load,submit

正如你所看到的事件能夠分爲兩類:一類爲用戶的行爲所致使的事件一類是瀏覽器的行爲所致使的事件。上面的簡單列表中:用戶執行click,mouseover,out和keypress事件;瀏覽器fires the load event when the page has finished loading, and the submit event when the user clicks a submit button(so it is almost a user-performed events)

Adding Event handlers:

An event is fired on a given DOM element.這意味着咱們能夠wireup一個event handler(或者event listener)to a given element.一個event handler是一個當一個給定事件發生時須要執行的函數。好比下面的代碼:

// <div id="some_button"> Click Me! </div>
var btn = document.getElementById("some_button");
function welcome_user(evt) {
alert("Hello there!");
}
btn.addEventListener("click", welcome_user, false);

在event中有一個概念bubbling and capturing:

<div>
<span> Click Here! </span>
</div>

看上面的代碼,若是咱們點擊span,由於span在div元素內,因此當咱們點擊span時,div元素也將被點擊。因此兩個元素div和span豆漿有一個click事件被觸發。若是兩個元素都有eventhandler偵聽,那麼豆漿被調用。問題是:誰先被執行?這就是capturing和bubbling的背景了。DOM標準說事件應該首先capture隨後bubble. Capture階段是當事件從最高級別的元素開始向下擴散。在這裏,click handler of div將被先執行。一旦event到了最底部,或者最裏面,event將被bubble。Bubble階段將從最底部不斷向上冒泡。咱們再回到addEventListener的第三個參數是用於指定是否在capture phase來處理。因爲IE9以前不支持capturing,那麼一般false是安全的(bubbling)。

相關文章
相關標籤/搜索