眾所周知,在js中對象就是精髓,不理解對象就是不理解js。
那么什么事js中的對象呢?
在js中,幾乎一切皆對象:
- Boolean ,String,Number可以是對象(或者說原生數據被認作對象);
- Dates ,Maths,Regexps,Arrays,Funcitons,當然Objects,這些都是對象;
JS中,所有值,除了原生值,都是對象;這些原生值包括:strings,numbers('3.14'),true,false,null和undefined
對象是包含變量的變量,js變量可以包含單個值,比如:
var person = "John Doe";
同樣,對象也是變量,但它可以包含很多的值,只不過這些值是以鍵值對的形式表現的(name and value separated by a colon),一個js對象就是命名值的集合。
var person = {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"};
對象屬性
被命名的值,在js對象中被稱為屬性;這種以命名值而寫的對象的寫法類似於:
Property Value firstName John lastName Doe age 50 eyeColor blue
- php的Associate Arrays
- python中的Dictionaries
- C中的Hash表
- Java中的hash maps
- Ruby和Perl中的Hashes
對象方法
方法是表現在對象上的行為或動作,對象屬性可以是原生值,別的對象,或者函數。一個對象的方法是一個對象包含一個函數定義的屬性。
fullName function() {return this.firstName + " " + this.lastName;}
js對象是一個包含所謂屬性的名值對和方法的容器。
創造一個js對象
在js中你可以定義和創造自己的對象,有不同的方法去創造對象:
- 利用對象字面量
- 利用new 關鍵字
- 定義一個構造器,然后實例化
- 在es5中,也可以利用Object.create()函數創造對象
對象字面量方法:最簡單,一句話定義並創造一個對象。對象字面量是鍵值對用大括號抱起來的系列。
var person = {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"};
new關鍵字方法:
var person = new Object(); person.firstName = "John"; person.lastName = "Doe"; person.age = 50; person.eyeColor = "blue";
以上兩種例子幾乎類似,沒必要用new Object()。為了簡潔,可讀性及執行性能,首選第一種(對象字面量方法)。
對象構造器
以上兩種方法在很多情境中有局限,它們只是創造一個單一對象。有時候,我們喜歡擁有一個可以創造很多一種類型對象的”對象類型“,此時利用對象構造函數創造一個對象類型的標准方式應運而生。
function person(first, last, age, eye) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eye;
}
var myFather = new person("John", "Doe", 50, "blue");
var myMother = new person("Sally", "Rally", 48, "green");
以上方法是一個對象構造器,一旦擁有了它,你可以創造同類的對象
var myFather = new person("John", "Doe", 50, "blue");
var myMother = new person("Sally", "Rally", 48, "green");
This關鍵字
在js中,被稱作this的東西,是一個“擁有”js代碼的對象。this的值,當用在一個函數里,是一個”擁有“函數的對象;當用在一個對象里,是對象本身。this關鍵字,在一個對象構造器里邊沒本身有值,僅僅是新對象的替代品。當構造器被用於構造對象時this的值會變成新對象。
注意:this不是一個變量,它是關鍵字,你不能改變this值。
js用於內置的原生對象的構造器
var x1 = new Object(); // A new Object object var x2 = new String(); // A new String object var x3 = new Number(); // A new Number object var x4 = new Boolean(); // A new Boolean object var x5 = new Array(); // A new Array object var x6 = new RegExp(); // A new RegExp object var x7 = new Function(); // A new Function object var x8 = new Date(); // A new Date object
Math()對象不在列表中,因為Math是一個全局對象,new關鍵字不能用在Math中。
並且,眾所周知,js擁有原生數據類型String,Number,和Boolean的對象版本。沒理由創造浮躁對象,原生值執行的更快。
var x1 = {}; // new object
var x2 = ""; // new primitive string
var x3 = 0; // new primitive number
var x4 = false; // new primitive boolean
var x5 = []; // new array object
var x6 = /()/ // new regexp object
var x7 = function(){}; // new function object
JS對象是可變的
對象可變,他們靠索引定位而非值。如果一個person是一個對象,那么以下語句不會創造person的副本。
var x = person; // This will not create a copy of person.
對象x不是person的副本,它是person本身,因此任何x的改變都很改變person。
js變量不可變,可變(Mutable)的只是js對象,如下實例。
var person = {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"}
var x = person;
x.age = 10; // This will change both x.age and person.age
對象屬性
屬性是js對象中最重要的部分
- 屬性是js對象所關聯的值
- js對象是無序屬性的集合
- 屬性可以增刪改查,有些是只讀
訪問js屬性語法:
objectName.property // person.age objectName["property"] // person["age"] objectName[expression] // x = "age"; person[x],表達式必須等於屬性名 person.firstname + " is " + person.age + " years old."; person["firstname"] + " is " + person["age"] + " years old.";
for-in循環遍歷對象屬性語法:
for (variable in object) {
code to be executed
}
添加屬性
person.nationality = "English";
但你不能用保留字作為屬性名,利用js命名規則。
刪除屬性
var person = {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"};
delete person.age; // or delete person["age"];
刪除關鍵字刪除了屬性值和其本身,刪除后屬性在其被再添加之前不能再用。刪除操作符被用於對象屬性的操作,對於變量或者方法無效。但謹記,delete操作符不應用於預定義js對象的屬性,這樣會阻塞應用。
屬性的屬性(property attributes)
屬性有個name,也有一個value。value是屬性的屬性之一,其它屬性是:enumerable,configurable,writable。這些屬性定義了屬性是如何被訪問的(是否可讀可寫)
在js中,所有屬性是可讀的但只有value的屬性可以被改變(當且僅當屬性是可寫的)。ES5中有針對getting和setting所有屬性的屬性的方法。
原型屬性
js對象繼承它們原型的屬性,delete關鍵字不刪除所繼承的屬性,但是如果你刪除了原型的屬性,這將影響所繼承原型的所有對象。
對象方法
如前所述,js方法是對象中表現的行為。js方法是包含函數定義的屬性,即方法是存做對象屬性的函數。
訪問對象方法:
methodName : function() { code lines }//創造一個對象方法
objectName.methodName();//訪問
name = person.fullName();//fullName屬性當以()調用時將執行
name = person.fullName;//fullName當么有()調用時將返回函數定義
利用內置方法
var message = "Hello world!"; var x = message.toUpperCase();//HELLO WORLD!
添加方法(類似於添加屬性)
function person(firstName, lastName, age, eyeColor) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.eyeColor = eyeColor;
this.changeName = function (name) {
this.lastName = name;
};
}
對象屬性
所有js對象擁有一個原型(prototype),原型也是對象。所有js對象繼承其原型的屬性和方法。
利用字面量或者newObject 構造的對象,繼承所謂的Object.prototype的原型;
利用new Date()構造的對象繼承Date.prototype.Object.prototype 出於原型鏈的頂端(top)
因此所有的js對象繼承自Object.prototype。
創造一個原型
標准方法是利用一個構造函數去創造一個對象原型:
function Person(first, last, age, eyecolor) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
}
有了構造函數,你可以利用new關鍵字去從同樣原型中創造新對象。
var myFather = new Person("John", "Doe", 50, "blue");
var myMother = new Person("Sally", "Rally", 48, "green");
//構造函數是Person對象的原型,首字母大寫去命名構造函數是一個好慣例
給對象添加屬性和方法
有時你想添加新屬性或方法給一個存在的對象,給所有給定類型的存在對象,或者給一個對象原型。
myFather.nationality = "English";//給一個存在的對象,僅僅對此對象
myFather.name = function () {
return this.firstName + " " + this.lastName;
};//添加一個方法,僅僅對此對象
給原型添加屬性
Person.nationality = "English";//不能像給已存在對象添加新屬性那樣給原型添加,因為原型不是一個存在的對象。
function Person(first, last, age, eyecolor) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
this.nationality = "English"
}//給原型添加屬性,必須添加在構造函數里邊。原型屬性可以擁有原型值(默認值)
function Person(first, last, age, eyecolor) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
this.name = function() {return this.firstName + " " + this.lastName;};
}//添加方法
利用原型屬性
js原型屬性允許你去給存在的原型添加新屬性和新方法,但要記住:只改變你所擁有的屬性,別去動標准js對象的屬性。
function Person(first, last, age, eyecolor) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
}
Person.prototype.nationality = "English";
Person.prototype.name = function() {
return this.firstName + " " + this.lastName;
};
js函數定義
js函數可以function關鍵字定義,你也可以用函數聲明(declaration)和函數表達式( expression)去定義一個函數。
函數聲明
function functionName(parameters) {
code to be executed
}
//分號被用於分離js執行語句,由於函數聲明不是一個可執行語句,因此以分號結束一個函數聲明並不常見
函數聲明不被立即執行,他們是“備用”,並且當調用時稍后執行。
函數表達式
var x = function (a, b) {return a * b};//函數表達式可以存儲在一個變量里
var z = x(4, 3);//此時,這個變量可被用作一個函數
//事實上,上述函數是一個匿名函數,存儲在變量中的函數不必擁有名字,他們用變量名調用;並且以分號結束是因為是可執行語句的一部分
函數構造器
函數可以利用js內嵌的函數構造器Function()定義
var myFunction = new Function("a", "b", "return a * b");
var x = myFunction(4, 3);
事實上你不必如此,在js中很不必用到new關鍵字。
函數提升
提升是js移動聲明到當前作用域頂端的默認行為,提升用於變量聲明和函數聲明,因此函數可以先調用后聲明。但是,函數表達式定義的函數不會被提升。
myFunction(5);//25
function myFunction(y) {
return y * y;
}
foo();//VM747:1 Uncaught TypeError: foo is not a function(…)
var foo=function(){}
自執行函數
(function () {
var x = "Hello!!"; // I will invoke myself
})();
事實上,上述函數是一個匿名的自執行函數
函數可被用在值中,也可用在表達式中
function myFunction(a, b) {
return a * b;
}
var x = myFunction(4, 3);
var y= myFunction(4, 3) * 2;
函數皆對象
typeof操作符在js中對於functions返回為‘function’,但是函數最好是被描述為對象,js函數擁有屬性和方法。
function myFunction(a, b) {
return arguments.length;
}//但函數被調用時返回參數個數
var txt = myFunction.toString();//頭String()方法返回一個字符串
函數作為一個對象的屬性定義,被稱作一個對象的方法;
函數作為創造對象的定義,被稱作一個對象構造器。
函數形參(parameters)
js函數對形參值不做任何檢查。
js形參和實參:形參是函數定義中的名(names),實參是傳給(或接收)函數的真實值(real values)
functionName(parameter1, parameter2, parameter3) {
code to be executed
}
形參規則:js針對形參的函數定義不區分數據類型,對傳來的實參不做類型檢查,對接收的實參數目不做檢查。
形參默認:如果函數丟失實參(少於聲明的)時調用,缺失的值被設置為:undefined。有時候可以接受,但最好對形參設置一個默認值。
function myFunction(x, y) {
if (y === undefined) {
y = 0;
}
}//如果函數被過多實參(多余聲明的)調用。這些實參可以利用實參對象獲取
實參對象
js擁有被稱作實參對象的內置對象,它們包含一個實參數組,當函數被調用時。此時可以簡單利用函數去在一個數據列表中尋找最值。
x = findMax(1, 123, 500, 115, 44, 88);
function findMax() {
var i;
var max = -Infinity;
for (i = 0; i < arguments.length; i++) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
}
//統計輸入值之和
x = sumAll(1, 123, 500, 115, 44, 88);
function sumAll() {
var i, sum = 0;
for (i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
}
按值傳遞的實參
形參,在函數調用中,就是函數實參;js實參按值傳遞,函數僅僅去知道值,而非實參地址。
如果一個函數改變了實參值,不會改變形參的原始值。形參的改變在函數外部不可見(不反映)。
對象按引用傳遞,js中對象索引是值,因此,對象表現為按索引傳遞:如果一個函數改變一個對象的屬性,這就改變了原始值。對象屬性的改變在函數外部可見(反映)
js函數調用
四種方式,每種方式以this如何初始化為區分。
函數調用:函數代碼當定義時不執行,僅當調用時執行。(a JavaScript function can be invoked without being called.)
作為函數調用函數
function myFunction(a, b) {
return a * b;
}
myFunction(10, 2); // myFunction(10, 2) will return 20
上述函數不屬於任何對象,但js中有一個默認的全局對象。在html中默認的全局對象是html頁面本身,因此上述函數”屬於“html頁面。在瀏覽器頁面對象是瀏覽器窗口,上述函數自動變成window 函數,木Function和window.沒有Function是一樣的
這是一個常用的調用函數的方式,但並不是好習慣。全局變量,方法,或者函數在全局對象中很容易造成命名沖突和bug。
全局對象
當一個函數沒有擁有者對象而被調用時,this值變成全局對象。在web瀏覽器中全局對象是browser window。
function myFunction() {
return this;
}
myFunction(); // Will return the window object
調用一個作為全局對象的函數,引起讓this值變為全局對象的后果。因此,用window對象作為變量很容易阻塞你的編程。
作為方法調用
作為方法調用
var myObject = {
firstName:"John",
lastName: "Doe",
fullName: function () {
return this.firstName + " " + this.lastName;
}
}
myObject.fullName(); // Will return "John Doe"
fullName方法是一個函數,這個函數屬於對象,沒有Object是該函數的擁有者。被稱作this的東西,是一個擁有js代碼的對象,此時的this值指向myObject。
var myObject = {
firstName:"John",
lastName: "Doe",
fullName: function () {
return this;
}
}
myObject.fullName(); // Will return [object Object] (the owner object);將函數作為對象的方法來調用,使得this指向對象本身
作為構造函數調用
如果一個函數以前置new關鍵字調用,他就是一個構造器的調用。看起來像創造一個新函數,但是由於js函數皆對象,你事實上創造了一個新對象。
// This is a function constructor:
function myFunction(arg1, arg2) {
this.firstName = arg1;
this.lastName = arg2;
}
// This creates a new object
var x = new myFunction("John","Doe");
x.firstName; // Will return "John"
構造器的調用創造了一個新對象,這個新對象繼承他的構造器的屬性和方法。this關鍵字在構造器中沒有一個值,但是在構造器被調用時,this值將是所創造的那個新的對象。
以函數的方法調用
js中函數皆對象,js函數擁有屬性和方法。call()和apply()是js的預定義函數方法,都可以調用函數,並且都必須將擁有者對象作為首個形參(調用時)。
function myFunction(a, b) {
return a * b;
}
myObject = myFunction.call(myObject, 10, 2); // Will return 20
function myFunction(a, b) {
return a * b;
}
myArray = [10, 2];
myObject = myFunction.apply(myObject, myArray); // Will also return 20
兩種方式均那擁有者對象(owner object)作為首個實參,唯一的區別是call分散地取函數實參,apply將函數實參作為一個數組來取。在js嚴格模式中,被調用的函數中首個實參是this的值,即使這個實參不是一個對象。在非嚴格模式中,如果首個實參是null或者undefined,它將被全局對象取代。利用call和apply你可以設置this值,並且將函數作為一個現有對象的方法調用。
js閉包
js變量可以屬於全局的或者局部的作用域,局部變量可利用閉包構造。
全局變量
一個函數可以訪問定義在其中的所有變量:
function myFunction() {
var a = 4;
return a * a;
}
並且,一個函數也可以訪問在其外層的函數,像這樣:
var a = 4;
function myFunction() {
return a * a;
}
此時a是一個全局變量,在頁面中全局變量屬於window對象,全局變量可被所有頁面中腳本利用和改變;第一個例子中a是局部變量,局部變量僅能在其所定義的函數內部用,對其他函數和腳本代碼隱藏。同名的全局和局部變量是不同的變量,改變一個不影響另一個,變量不以var關鍵字聲明的通常是全局的,即使在函數內部。
變量生命周期
全局變量和你的應用程序,你的window,你的webpage同生,局部變量短命,當函數調用時被造就,當調用結束被刪除。
看以下例子
var counter = 0;
function add() {
counter += 1;
}
add();
add();
add();
// the counter is now equal to 3
function add() {
var counter = 0;
counter += 1;
}
add();
add();
add();
// the counter should now be 3, but it does not work !
function add() {
var counter = 0;
function plus() {counter += 1;}
plus();
return counter;
}//solve this problem
js閉包(closure)
還記得自我調用函數嗎?這個函數干嘛的?
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
add();
add();
add();
// the counter is now 3
解釋:變量add作為一個自我調用函數的返回值聲明,自執行函數僅運行一次,設置counter為0,返回表達式。這種方式的add變成一個函數,完美的部分是它可以訪問父級作用域內的counter.這就是所謂的js閉包,它讓函數擁有私有變量成為可能。counter被匿名函數的作用域所保護,並且只能利用add函數去改變。
總之:閉包是一個即使父級函數關閉,仍可以訪問父級作用域的函數。
參考文獻:http://www.w3schools.com/
