標籤:creat nts called 命名 統計輸入 值傳遞 this關鍵字 blog without
眾所周知,在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對象中被稱為屬性;這種以命名值而寫的對象的寫法類似於:
PropertyValuefirstNameJohnlastNameDoeage50eyeColorblue
- php的Associate Arrays
- python中的Dictionaries
- C中的Hash表
- Java中的hash maps
- Ruby和Perl中的Hashes
對象方法
方法是表現在對象上的行為或動作,對象屬性可以是原生值,別的對象,或者函數。一個對象的方法是一個對象包含一個函數定義的屬性。
fullNamefunction() {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 objectvar x2 = new String(); // A new String objectvar x3 = new Number(); // A new Number objectvar x4 = new Boolean(); // A new Boolean objectvar x5 = new Array(); // A new Array objectvarx6 = new RegExp(); // A new RegExp objectvar x7 = new Function(); // A new Function objectvar x8 = new Date(); // A new Date object
Math()對象不在列表中,因為Math是一個全域對象,new關鍵字不能用在Math中。
並且,眾所周知,js擁有原生資料類型String,Number,和Boolean的對象版本。沒理由創造浮躁對象,原生值執行的更快。
var x1 = {}; // new objectvar x2 = ""; // new primitive stringvar x3 = 0; // new primitive numbervar x4 = false; // new primitive booleanvar x5 = []; // new arrayobjectvarx6 = /()/ // new regexp objectvar 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.ageobjectName["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);//25function 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;}// Thiscreates a new objectvar 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 20function myFunction(a, b) { return a * b;}myArray = [10, 2];myObject = myFunction.apply(myObject, myArray); // Will also return 20
兩種方式均那擁有者對象(owner object)作為首個實參,唯一的區別是call分散地取函數實參,apply將函數實參作為一個數組來取。在jsstrict 模式中,被調用的函數中首個實參是this的值,即使這個實參不是一個對象。在非strict 模式中,如果首個實參是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 3function 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/
JS中的對象和方法簡單剖析