值的轉(zhuǎn)換

什么是表達式?
表達式是js的一個短語,js解析器會將其計算出一個結(jié)果。
例如:程序中的常量是最簡單的一種表達式,變量也是一種表達式,他的值就是賦值給變量的值。復(fù)雜表達式由簡單表達式構(gòu)成。比如數(shù)組訪問表達式(是由表示一個數(shù)組的表達式、左方括號,整數(shù),右方括號組成,eg:arr[0]);同樣的函數(shù)調(diào)用表達式(是由一個表示函數(shù)對象的表達式和0個或者多個參數(shù)構(gòu)成)
將簡單的表達式組合成復(fù)雜的表達式最常用的方法是使用運算符。運算符根據(jù)特定的規(guī)則對操作數(shù)(通常兩個)進行運算,并計算出新值。eg:x*y
表達式分為以下幾種
1.原始表達式
1.1什么是原始表達式
最簡單的表達式就是原始表達式,原始表達式是表達式中最小的單位—他們不包含其他表達式。原始表達式包含常量,變量,關(guān)鍵字,直接量
直接量是直接在程序中出現(xiàn)的常數(shù)值
eg:
'hello' //字符串直接量? ?
12 //整型直接量
/pattern/ //正則表達式直接量
3.14 //浮點類型直接量
變量
變量是一個符號名稱,可以通過名稱獲得對值的引,變量首字母必須由字母,下劃線,$開始,后續(xù)的字符可以是字母、下劃線、數(shù)字、$,也可以用非英語語言或者數(shù)學(xué)符號來書寫標(biāo)識符,變量可以賦予不同類型的值
eg:
i? //返回變量i的值
sum //返回變量sum的值
undefined? //undefined全局變量
關(guān)鍵字(js中的一些保留字構(gòu)成了原始表達式)
eg:
true? //返回一個布爾值 真
false //返回一個布爾值假
null //返回一個空值
this //返回當(dāng)前對象
2.對象和數(shù)組初始化表達式
2.1什么是對象和數(shù)組初始化表達式?。
對象和數(shù)組初始化表達式實際上是一個新創(chuàng)建的對象和數(shù)組。這些初始化表達式成為‘對象直接量’和‘?dāng)?shù)組直接量’
2.2 數(shù)組初始化表達式
數(shù)組初始化表達式是通過一堆方括號和其內(nèi)的逗號隔開的列表構(gòu)成,數(shù)組初始化的表達式中的元素可以是任意類型的值,也可以數(shù)組套數(shù)組
eg
[ ] //一個空的數(shù)組
[1+2,2+3]//用于兩個元素的數(shù)組
[[1,2,3],[4,5,6],{title:'標(biāo)題1',num:2},null,undefined] //擁有5個元素的數(shù)組
注:數(shù)組直接量中的列表逗號之前的元素可以直接省略,這時省略的空位會填充undefined
let? arr=[1,,,5]//這數(shù)組包含5個元素,其中三個是undefined
2.2 對象初始化表達式
對象初始化表達式和數(shù)組初始化表達式非常類似,只是方括號由花括號代替,并且每個子表達式都包含一個屬性名和一個冒號作為前綴
eg:
let p={x:2.3,y:-1.2}
對象直接量也可以嵌套
let square={left:{x:2,y:3}}
3.函數(shù)定義表達式
函數(shù)定義表達式定義一個js函數(shù),函數(shù)定義表達式可稱為‘函數(shù)直接量’。一個典型的函數(shù)定義表達式包含關(guān)鍵字function,跟隨其后是一對圓括號,括號內(nèi)是一個以逗號隔開的列表,列表包含0個或者多個標(biāo)識符(參數(shù)名),然后再跟隨一個由花括號包裹的js代碼段(函數(shù)體),函數(shù)定義表達式同樣可以包含函數(shù)名字。函數(shù)也可以通過函數(shù)語句來定義,而不是函數(shù)表達式
eg:
var square = function(x) { return x*x}
4.屬性訪問表達式
屬性訪問表達式運算得到一個對象的屬性或者一個元素的值。
js定義的兩種語法
*第一種對象表達式后面緊跟一個句點和標(biāo)識符
let o={x:1,y:{z:3}}
let arr=[o,4,[5,6]]
o.x //o的x屬性的值
o.y.z //o.y的屬性z的值
*第二種寫法是使用方括號,方括號內(nèi)是另外一種表達式(這種方法適用于對象和數(shù)組)。第二個表達式指定要訪問的屬性名稱或者代表要訪問的數(shù)組元素的索引
eg:
o["x"]//o的x屬性的值
arr[1] //表達式arr中索引值為1的元素
arr[0].x //達式arr中索引值為0的元素的x屬性
注:如果對象表達式跟隨一對方括號,則會計算方括號內(nèi)的表達式的值并將它轉(zhuǎn)為字符串,如果命名的屬性不存,那么整個屬性表達式的值是undefined
5.調(diào)用表達式
調(diào)用表達式是一種調(diào)用(或執(zhí)行)函數(shù)或方法的語法表示。
它是一個函數(shù)表達式開始,這個函數(shù)表達式指定了要調(diào)用的函數(shù)。函數(shù)表達式后跟隨一個圓括號,括號內(nèi)是一個以逗號隔開的參數(shù)列表,參數(shù)可以是0個也可以是多個
eg:
f(0);
Math.max(x,y,z);
a.sort();
當(dāng)對調(diào)用表達式進行求值時,首先計算函數(shù)表達式,然后計算參數(shù)表達式,得到一組參數(shù)值。然后實參的值依次賦給給形參,這些形參是定義函數(shù)時指定的,接下來開始執(zhí)行函數(shù)體。如果函數(shù)使用return語句給出一個返回值,那么這個值就是函數(shù)調(diào)用表達式的值。否則就是undefined。 如果函數(shù)表達式的值不是一個可調(diào)用的對象,則返回語法錯誤(所有函數(shù)默認返回值都是未定義)
?function fn1() {
? ? ? ? ? ? return 100;
? ? ? ? }
? ? ? ?console.log(fn1());//100
? ? ? let person = {
? ? ? ? ? ? name: 'jiaojiao',
? ? ? ? ? ? say: function () {
? ? ? ? ? ? ? ? console.log(this.name)
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? person.say();//jiaojiao 這里的this指向person
? ? ? ? console.log(person.say());//undefined
eg:
如果這個表達式是一個屬性訪問的表達式,這個調(diào)用成為方法調(diào)用,方法內(nèi)的this指向其宿主對象
eg:
?let person={
? ? ? ? ? ?name:'jiaojiao',
? ? ? ? ? ?say:function(){
? ? ? ? ? ? ? ?console.log(this.name)
? ? ? ? ? ?}
? ? ? ?}
? ? ? ?person.say();//jiaojiao 這里的this指向person
如果是全局函數(shù),this指向window,在嚴(yán)格模式下this是undefined
eg:
function func() {
? ? ? ? ? ? console.log(this)//window
? ? ? ? }
func();? ? ??
? ?function func() {
? ? ? ? ? ? "use strict";
? ? ? ? ? ? console.log(this)//undefined
? ? ? ? }
? ? ? ? func();
6.對象創(chuàng)建表達式
對象創(chuàng)建表達式創(chuàng)建一個對象創(chuàng)建一個對象并調(diào)用一個函數(shù)(這個函數(shù)被稱為構(gòu)造函數(shù))初始化新對象的屬性。對象創(chuàng)建表達式和函數(shù)表達式非常類似,只是對象創(chuàng)建表達式之前多了一個關(guān)鍵字new
new Object()
new Proint(2,3)
如果一個對象創(chuàng)建表達式不需要傳入?yún)?shù)給構(gòu)造函數(shù)的話,那么這對空圓括號是可以省略的
new Object
new Date
當(dāng)計算一個對象創(chuàng)建表達式時,和對象初始化化通過{}創(chuàng)建對象的做法一樣,js首先創(chuàng)建一個新的空的對象,然后,js通過傳入指定參數(shù)并將這個新對象當(dāng)做this的值調(diào)用一個指定函數(shù)。這個函數(shù)可以使用this來初始化這個新創(chuàng)建的對象的屬性。那些被當(dāng)做構(gòu)造函數(shù)的函數(shù)不會返回一個值,并且這個新創(chuàng)建并初始化后的對象就是整個對象創(chuàng)建表達式的值。如果一個構(gòu)造函數(shù)確實返回了一個對象值,那么這個對象就作為整個對象創(chuàng)建表達式的值,而新創(chuàng)建的對象就廢棄了
7.運算符的概述
js中的運算符用于算術(shù)表達式,比較表達式,賦值表達式等。下面表格列出了js的運算符,作為一分方便的參照
注:大多數(shù)的運算符由標(biāo)點符號表示的。另外一些由關(guān)鍵字表示的,比如delete和instanceof
下面的表格是按照運算符的優(yōu)先級排序的,前面的運算符優(yōu)先級要高于后面的運算符優(yōu)先級。被水平線隔開的運算符具有不同的優(yōu)先級,標(biāo)題A列表示運算符的結(jié)合性,L(從左至右),R(從右至左),標(biāo)題N表示操作數(shù)的個數(shù)。標(biāo)題類型表示期望操作的類型,以及運算符結(jié)果類型
注:lval左值

7.1操作數(shù)的個數(shù)
運算符可以根據(jù)其操作數(shù)的個數(shù)進行分類,一個操作數(shù)是一元運算符,兩個操作數(shù)的是二元運算符,三個操作數(shù)的是三元運算符
一元運算符

三元運算符

其他的都是二元運算符
7.2操作數(shù)類型和結(jié)果類型
一些運算符可以作用于任何類型,但仍然希望它們的操作數(shù)是指定類型的數(shù)據(jù),并且大多數(shù)運算符返回一個特定類型的值。如表運算符標(biāo)題為‘類型’的列列出了運算符操作數(shù)的類型(箭頭前)和運算結(jié)果的類型(箭頭后)。
eg:? console.log("3"*"5");//15
"3"*"5" 表達式的值是數(shù)字15,是因為乘法運算符“*”希望操作數(shù)是數(shù)字,js將操作數(shù)轉(zhuǎn)換為數(shù)字了
eg:?console.log(1+"11");//111
1+"11"表達式的值是字符串111,js將操作數(shù)1轉(zhuǎn)換為字符串‘1’
還有很多類型的轉(zhuǎn)換,后面會充分解釋在什么情況下回轉(zhuǎn)換
7.3 左值
左值是指"表達式只能出現(xiàn)在運算符的左側(cè)”,在js中,變量,對象屬性和數(shù)組元素均是左值。在es規(guī)范允許內(nèi)置函數(shù)返回一個左值,但是自定義函數(shù)不能返回左值
7.4 運算符的副作用
計算一個簡單的表達式(比如2*3)不會對程序的運行狀態(tài)造成任何影響,程序后續(xù)執(zhí)行的計算也不會受到該計算的影響。而有一些表達式有很多的副作用,前后表達式運算會相互影響。賦值運算符是最明顯的一個例子:如果給一個變量賦值或?qū)傩再x值,那么使用這個變量或?qū)傩缘谋磉_式的值會發(fā)生改變。比如‘++’和‘--’,因為他們包含隱式的賦值。delete運算符同樣有副作用,刪除一個屬性就像(但是不完全一樣)給這個屬性賦值undefined
其他的js運算符都沒有副作用,但函數(shù)調(diào)用表達式和對象創(chuàng)建表達式有些特別,在函數(shù)體或者構(gòu)造函數(shù)內(nèi)部運用可這些運算符并產(chǎn)生副作用的時候,我們說函數(shù)掉用表達式和對象創(chuàng)建表達式是有副作用的
7.5 運算符優(yōu)先級
1.表7-1中運算符的優(yōu)先級是從高到低,。每個水平分割線內(nèi)的一組運算符具有相同的優(yōu)先級。運算符的優(yōu)先級控制這運算符的執(zhí)行順序。優(yōu)先級高的要高于優(yōu)先級低的
eg:看下面的例子,因為乘法運算符(*)高于加法運算符+(),賦值運算符“=”是最低的,所以先執(zhí)行y*z,然后執(zhí)行加法+x,最后執(zhí)行賦值
w = x+y*z
2.運算符的優(yōu)先級可以通過圓括號來重寫。為了讓加法先執(zhí)行,我們可以這樣寫
w=(x+y)*z
3.屬性訪問表達式和調(diào)用表達式的優(yōu)先級比任何運算符的優(yōu)先級都搞。eg:
typeof my.functions[x](y);
盡管typeof是優(yōu)先級最高的運算符之一,但是typeof 也是在兩次屬性訪問和函數(shù)調(diào)用后執(zhí)行
注:實際上,如果你真的不確定你所使用的運算符的優(yōu)先級,最簡單的方法用圓括號強行指定運算次序。有些重要規(guī)則需要熟記,乘法和除法的優(yōu)先級高于加法和減法,賦值運算符的優(yōu)先級非常低,通常最后執(zhí)行
7.6 運算符的結(jié)合性
在“表7-1”標(biāo)題為A的列說明的運算符的結(jié)合性。L是從左到右結(jié)合(從左到右的順序?qū)崿F(xiàn)),R是從右到左結(jié)合(從右想左的順序執(zhí)行),結(jié)合性指定了在多個具有同樣優(yōu)先級的運算符表達式的運算順序。
eg:下面的表達式一樣
w=x-y-z? ? ? ------? ? w=((x-y)-z)
x=~-y? ? -----? ? ? ? ?x=~(-y)
w=x=y=z? -----? w=(x=(y=z))
q= a?b:c?d:e?f:g? ?---------??q= a?b:(c?d:(e?f:g ))
因為一元運算符,賦值和三元條件運算符都具有從右向左的結(jié)合性
7.7運算順序
運算符的優(yōu)先級和結(jié)合性規(guī)定了他們在復(fù)雜的表達式的運算順序,但是沒有規(guī)定子表達式的計算過程順序。js總是嚴(yán)格按照從左至右的順序來計算表達式,eg:w=x+y*z中,先計算子表達式w,然后計算x,y,z的值,然后在計算y*z ,再加上x的值,最后將其賦值給表達式w所指代的變量和屬性。給表達式添加圓括號會改變乘法,加法個賦值運算的關(guān)系,但是從左到的順序是不會變得。
只有在任何一個表達式具有副作用影響到其他表達式時候,其求值順序才會看上有所不同,如果表達式x中的一個變量自增1,這個變量在表達式z中使用,那么實際是先計算出x的值,在計算z的值
eg:
?let a=1;
?let b=(a++)+a;
?console.log(b)//3
他的運算順序是?
1. 計算b?
2. 計算a++ //1
3. 計算 a // 2
4 . 計算 (a++) +a? 也就是1+2
5.把結(jié)果賦值給b
8算術(shù)表達式
本節(jié)涵蓋了那些進行算術(shù)計算的運算符,以及對操作數(shù)的算術(shù)操作。乘法,除法和減法運算符非常簡單,我們首先講解它們。加法運算符單獨占一節(jié),因為加法運算符可以做字符串的連接操作,并且類型轉(zhuǎn)換有些特殊。一元運算符和位運算符同樣在單獨的兩節(jié)中講到
基本運算符*(乘法),/(除法),%(求余),-(減法)
這四個運算符很簡單,會把非數(shù)字的操作數(shù)轉(zhuǎn)換為數(shù)字,然后求積,商,余數(shù),差。那些無法轉(zhuǎn)為數(shù)字的操作數(shù)轉(zhuǎn)為NaN,如果操作數(shù)(或者轉(zhuǎn)換結(jié)果)是NaN,算術(shù)的運算結(jié)果也是NaN , 0/0也是NaN。任何數(shù)除以0運算結(jié)果是父無窮大或者正無窮大
eg:
? ? ? let obj={x:1,y:2};
? ? ? ? console.log(obj*3);//NaN
? ? ? ? console.log('hhh'-3);//NaN
? ? ? ? console.log(0/0);//NaN
? ? ? ? console.log(11/0);//Infinity
? ? ? ? console.log(true-1);//0
? ? ? ? console.log(false-1);//-1
? ? ? ? console.log(true*10);//10
8.1 '+'運算符
二元加法運算符“+”可以對兩個數(shù)字做加法,也可以做字符串連接操作
1.當(dāng)兩個操作數(shù)都是數(shù)字時做加法運算,兩個都是字符串時做字符串連接
console.log(1+2);//3
console.log('hello'+' jiaojiao');// hello jiaojiao
console.log('1'+'2');//12
加法運算符優(yōu)先考慮字符串的連接,如果其中一個操作符是字符串或者轉(zhuǎn)換為字符串的對象,則另外一個操作數(shù)會轉(zhuǎn)為字符串,加法將進行字符串的連接
注:其中一個操作數(shù)是對象,則對象會遵循對象到原始值的轉(zhuǎn)換規(guī)則轉(zhuǎn)換為原始值,日期對象通過toString()方法進行轉(zhuǎn)換,其他的對象則通過valueOf()的方法進行轉(zhuǎn)換。如果不是原始值,再通過toString()轉(zhuǎn)換,
在進行了對象帶原始值的轉(zhuǎn)換后,如果其中一個操作數(shù)是字符串,另一個操作數(shù)也會轉(zhuǎn)為字符串,然后進行字符串連接?
否則,兩個操作數(shù)都會轉(zhuǎn)為數(shù)字或者NaN,然后進行加法操作
? ? ? console.log(1+2);//3
? ? ? ? console.log("1"+"2");//12
? ? ? ? console.log("1"+2);//12
? ? ? ? console.log(1+{});//1[object object]
? ? ? ? console.log(true+true);//2
? ? ? ? console.log(2+null);//2
? ? ? ? console.log(2+undefined);//NaN
注:需要特別注意的是,當(dāng)加法運算符和字符串一起使用時,需要考慮加法的結(jié)合性的對運算順序的影響。也就是說,運算結(jié)果是醫(yī)院與運算符的運算順序,例如以下案例,第一行沒有圓括號,“+”運算符具有從左到右的結(jié)合性,因此兩個數(shù)字先進行加法運算,運算結(jié)果和字符串進行連接。第二行中,圓括號改變了運算順序,數(shù)字2和字符串連接,生成一個新的字符串,然后數(shù)字1再跟這個新的字符串進行連接,生成最終的結(jié)果
console.log(1+2+" hello jiaojiao") //3 hello jiaojiao
console.log(1+(2+" hello jiaojiao"));//12 hello jiaojiao
8.2 一元算術(shù)運算符
一元算術(shù)運算符作用于一個單獨的操作數(shù),并產(chǎn)生一個新的值。在js中,一元運算符有很高的優(yōu)先級,都是右結(jié)合
+,-是一元運算符,也是二元運算符
8.2.1 一元加法運算符(+)
一元加法運算符把操作數(shù)轉(zhuǎn)換為數(shù)字(或者NaN) , 并返回這個轉(zhuǎn)換后的數(shù)字。如果操作數(shù)本身就是數(shù)字,則直接返回這個數(shù)字
8.2.2 一元減法運算符(-)
當(dāng)“-” 用作一元運算符時,它會根據(jù)需要把操作數(shù)轉(zhuǎn)換為數(shù)字,然后改變運算結(jié)果的符號
8.2.3 遞增(++)運算符
遞增(++)運算符對其操作數(shù)進行加一操作,操作數(shù)是一個左值(變量,數(shù)組元素或者的對象屬性),運算符將操作數(shù)轉(zhuǎn)換為數(shù)字,然后數(shù)字加一,并將加一后的數(shù)值重新賦值給變量,數(shù)組元素或者對象屬性
遞增“++”運算符的返回值依賴于它操作數(shù)的位置。
1.當(dāng)運算符在操作數(shù)之前,稱為“前增量”運算符,它對操作數(shù)進行增量計算,并返回計算后的值
2.當(dāng)運算符在操作數(shù)之后,稱為“后增量”運算符,他對操作數(shù)進行增量計算,并未返回?計算后的值
兩者的區(qū)別在于 eg:
let i=1,j=++i;//i=2,j=2
let i=1,j=i++; // i=2,j=1
需要注意的是,表達式++x并不綜合x=x+1完全一樣,如一下案例,因為++運算符會把操作數(shù)轉(zhuǎn)為數(shù)字
eg:
let??x="1"
x=x+"1" //x為 字符串11
++x;//x為2?
8.2.4 遞減(--)運算符
遞減(--)運算符的操作數(shù)也是一個左值。它把操作數(shù)轉(zhuǎn)換為數(shù)字,然后減一,并將計算的值重新賦值。和遞增運算符一樣,遞減運算符的返回值依賴于他對操作數(shù)的位置,當(dāng)遞減(--)運算符在操作數(shù)之前,操作數(shù)減一,并返回減一之后的值,當(dāng)遞減(--)運算符在操作數(shù)之后,操作數(shù)減一,并未返回減一之后的值
9.關(guān)系表達式
關(guān)系運算符用于測試兩個值之前的關(guān)系比如(‘等于’,‘小于’,‘是...屬性’),根據(jù)關(guān)系是否存在而返回true或false。關(guān)系表達式總是返回一個布爾值,通常在if,while或者for語句中使用關(guān)系表達式,用以控制程序的執(zhí)行流程。
9.1相等和不等運算符
9.1.1 嚴(yán)格相等“===”運算符
?嚴(yán)格相等“===”運算符也叫恒等運算符,用來比較兩個值是否嚴(yán)格相等,在比較的過程中沒有任何類型的轉(zhuǎn)變,來檢測兩個操作數(shù)的值和類型是否相等,如果操作數(shù)相等返回true,否則返回false,
eg:
● 如果兩個值的類型不同,則它們不相等
console.log("1" === 1) //false
● 如果一個值是null,一個是undefined ,則它們不相等
console.log(null === undefined) //false
●? 如果其中一個值是NaN,或者兩個值都是NaN,它們不相等,NaN和任何值不相等,包括它本身
console.log(NaN === NaN) //false
● 如果兩個值為數(shù)字且數(shù)值相等,則它們相等。如果一個值為0,另一個值為-0,它們同樣相等
console.log(1===1.0)//true
console.log(0 === -0)//true
●如果兩個值為字符串,如果它們的長度和內(nèi)容相等,則它們相等
●如果兩個引用值指向同一個對象,數(shù)組或者函數(shù),則它們相等。如果指向不同的對象,則它們是不等,盡管兩個對象具有完全一樣的屬性
9.1.3 相等“===”運算符
相等“==”運算符,用來比較兩個值是否相等,相等則返回true,不等則返回false,這里的“相等”定義非常寬松,可以允許值的轉(zhuǎn)換
●如果兩個操作數(shù)類型相同,則和嚴(yán)格相等的比較規(guī)則一樣,如果嚴(yán)格相等,那么比較結(jié)果相等,如果不嚴(yán)格相等,則比較結(jié)果為不相等
●如果兩個操作數(shù)類型不相同,‘==’相等操作符也可能認為它們相等。檢測相等會遵循如下規(guī)則進行轉(zhuǎn)換
---如果一個值是null,另一個是undefined,則它們相等
---如果一個值是數(shù)字,另一個是字符串,先將字符串轉(zhuǎn)換為數(shù)字,然后使用轉(zhuǎn)換的值進行比較
---如果其中一個值是true,則將其轉(zhuǎn)換為1再進行比較。如果其中一個值是false,則將其轉(zhuǎn)換為0再進行比較
---如果一個是對象,一個是數(shù)字或者字符串,將對象轉(zhuǎn)為原始值進行比較。對象通過toString()方法或者valueOf()轉(zhuǎn)為原始值
---其他類型的比較則不相等
9.1.4 不相等“!=”運算符
不相等“!=”運算符檢測規(guī)則是相等‘==’運算符結(jié)果取反
9.1.5 不全等相等“!==”運算符
不全等相等“!==”運算符檢測規(guī)則是全等‘===’運算符結(jié)果取反
9.2比較運算符
比較運算符用來檢測兩個操作數(shù)的大小關(guān)系(數(shù)值的大小或者字母表的大?。?/p>
● 小于(<)?
如果第一個操作數(shù)小于第二個操作數(shù),則“<”運算符的運算結(jié)果為true,否則為false
●大于(>)?
如果第一個操作數(shù)大于第二個操作數(shù),則“>”運算符的運算結(jié)果為true,否則為false
●小于等于(<=)
如果第一個操作數(shù)小于等于第二個操作數(shù),則“<=”運算符的運算結(jié)果為true,否則為false
●大于等于(>=)
如果第一個操作數(shù)大于等于第二個操作數(shù),則“>=”運算符的運算結(jié)果為true,否則為false
比較運算符的操作數(shù)可能是任意類型。然而,只有數(shù)字和字符串才能真正執(zhí)行比較操作,因此那些不是數(shù)字和字符串類型的操作數(shù),將進行類型轉(zhuǎn)換,轉(zhuǎn)換規(guī)則如下
●如果操作數(shù)為對象,那么對象轉(zhuǎn)換為原始值進行比較 ,如果vlaueOf()返回一個原始值,那么直接使用這個原始值。否則,使用toString()的轉(zhuǎn)換結(jié)果進行比較操作
●在對象轉(zhuǎn)為原始值之后,如果兩個操作數(shù)都是字符串,那么將依照字母表的順序?qū)蓚€字符串進行比較,這里提到的‘字母表順序’是指組成這個字符串的16位Unicode字符的索引順序
●在對象轉(zhuǎn)換為原始值之后,如果至少有一個操作數(shù)不是字符串,那么兩個操作數(shù)轉(zhuǎn)為數(shù)字進行比較
注:0和-0相等,Infinity比其他任何數(shù)字都大(除了本身),-Infinity比其他任何數(shù)字都?。ǔ怂旧恚H绻渲幸粋€操作數(shù)轉(zhuǎn)換為NaN,那么比較操作符總是轉(zhuǎn)為false
●如果兩個操作數(shù),一個是數(shù)字,一個是字符串,比較運算符會先將字符串轉(zhuǎn)為數(shù)字再進行比較
9.3 in運算符
in操作符用來檢測in操作符用來判斷某個屬性屬于某個對象,對于數(shù)組屬性需要指定數(shù)字形式的索引值來表示數(shù)組的屬性名稱
in運算符希望它的左操作符是一個字符串或可以轉(zhuǎn)為字符串,右側(cè)的操作數(shù)是一個對象。如果右側(cè)對象擁有一個名為左操作數(shù)值的屬性名,那么表達式返回true
var point={
? ? x:1,
? ? y:2
}
"x" in point //true
"z" in point //false
'toString' in point //true
var data=[7,9,9];
'0' in data //true
1 in data //true 數(shù)字轉(zhuǎn)為字符串
'3' in data //false
9.4 instanceof 運算符
instanceof是測試它左邊的對象是否是它右邊的類的實例,返回 boolean 的數(shù)據(jù)類型
它的左操作數(shù)是一個對象,右操作數(shù)是一個類,如果左操作數(shù)是右操作數(shù)的實例則返回true,否則返回false
let d=new Date();
? ? ? console.log( d instanceof Date) ;//true d是由Date創(chuàng)建的
? ? ? console.log( d instanceof Object) ;//true Object是所有對象的實例
? ? ? console.log( d instanceof Number) ;//false d不是number對象
? ? ? let a=[1,2,3];
? ? ? console.log( a instanceof Array );//true a是一個對象
? ? ? console.log( a instanceof Object );//false 所有的數(shù)組都是對象
注:所有的對象都是Object實例
為了了解instanceof運算符是如何工作的,首先必須了解原型鏈,原型鏈作為js的繼承機制,為了計算 o instanceof f ,js首先計算f.prototype,然后再原型鏈查找o,如果找到o是f(或者f的父類)的一個實例,表達式返回true,否則返回false
10.邏輯表達式
邏輯運算符“&&”,“II”和“!”是對操作數(shù)進行布爾值的算術(shù)運算,經(jīng)常和關(guān)系運算符一起配合使用,邏輯運算符將多個關(guān)系表達式組合起來組合成為一個更復(fù)雜的表達式
10.1邏輯與(&&)
"&&"運算符可以從3個不停層次進行理解
1.當(dāng)操作數(shù)都是布爾值時,"&&"對兩個布爾值進行and操作,只有兩個值都為true的時候,它才返回true,如果其中一個值是false,它返回false
x==0 && y==0 //只有在x和y都為true的時候,才返回true
2.當(dāng)操作數(shù)不是布爾值的時候,有些值可以是真值,有些值是假值
假值 false、null、0、-0、""、undefined 和 NaN,其他為真值
如果兩個操作數(shù)都是真值,則返回一個真值,否則只是有一個操作數(shù)是假值,則返回假值
3.上文中我們提到運算符會返回一個真值或者假值,但是沒有說這個真值或者假值到底是什么值?
&&運算符首先計算左側(cè)的值,如果左側(cè)值為假值,則直接返回左側(cè)值,則不會計算右側(cè)的值
如果左側(cè)的值是真值,會計算表達式右側(cè)的值,如果右側(cè)的值是真值,則整個表達式的值一定是真值,否則整個表達式的值是假值
? ?let o={
? ? ? ? ? x:1
? ? ? }
? ? ? let p =null;
? ? ? console.log(o&&o.x)//1 返回o.x ,o是真值,o.x也是真值,o&&o.x也是真值
? ? ? console.log(p&&p.x)//返回null ,p是價值因為直接返回p
console.log(o&&o.z)//undefined?o是真值,o.z是假值,undefined是假值
10.2邏輯或(||)
"||"運算符對兩個操作數(shù)做布爾值或運算。如果其中一個或者兩個操作數(shù)是真值,它返回一個真值,如果兩個操作數(shù)都是假值,則返回一個假值。和(&&)運算符一樣,首先會計算左側(cè)的表達式,如果計算結(jié)果為真值,直接返回真值,不會計算右側(cè),如果為假值,才會計算右側(cè),右側(cè)是真值,則返回真值,是假值返回假值,例如一些案例
/*如果max_width定義 ,max是max_width,如果max_width每天定義,preferences.max_width定義了max是preferences.max_width,否則max的值是500*/
let max=max_width || preferences.max_width || 500;
這種慣用法通常用于函數(shù)體內(nèi),用來提供默認值
?function copy(o,p){
? ? ? ?p= p || {};//如果p沒有傳入人格對象,則使用一個新建對象
? ?}
10.3邏輯非
"!"運算符是一元運算符。它放置在一個單獨的操作數(shù)之前。它的目的是將操作數(shù)的布爾值求反。例如,如果x是真值,則!x返回false;如果x是價值則!x返回true
"!"邏輯非首先將其操作數(shù)轉(zhuǎn)換為布爾值,任何對布爾值取反
11.賦值表達式
"=" 運算符來給變量或者屬性賦值
"=" 運算符 它的左側(cè)操作數(shù)是一個變量或者對象屬性(或者數(shù)組的元素)。它的右側(cè)操作數(shù)可以是任意值。賦值表達式的值就是右側(cè)操作數(shù)的值。
賦值表達式的副作用是,有操作數(shù)的值賦值給左側(cè)的變量或?qū)ο髮傩?,這樣的話,后續(xù)對這個變量和對象屬性的引用都將得到這個值,這樣的話,后續(xù)對這個變量和對象屬性的引用都將得到這個值
賦值操作符的結(jié)合性是從右向左
i=j=k=0? ?這個表達式等于? ?i=(j=(k=0))
帶操作的賦值運算
除了常規(guī)的賦值運算符,js還支持其他的賦值運算符。eg,"+="執(zhí)行的是加法運算和賦值操作
total+=sales_tax
等價于
total=tatal+sales_tax
運算符"+="可以用于數(shù)字或者字符串,如果其操作數(shù)是數(shù)字,它將執(zhí)行加法運算和賦值操作,如果是字符串,它執(zhí)行字符串的連接
這類運算符還包括“-=”,“*=”,“+=”,"/=","%="
let data=[1,2,3,4];
let i=0;
console.log(data[i++] *= 2) ? ?;//2
?console.log( data[i++]=data[i++]*2) ?;//4
12.表達式計算
js可以通過函數(shù)eval()來解析運行一個js源碼組成的字符串,并返回一個值
eval()是一個函數(shù),但是它已經(jīng)當(dāng)做運算符來對待了
eval()只有一個值。
12.1如果傳入的參數(shù)是字符串,它會將字符串當(dāng)做js編碼來編譯
eg:? ? ??
let obj=`[
? ? { "name":"runoob" , "url":"www.runoob.com" },
? ? { "name":"google" , "url":"www.google.com" },
? ? { "name":"微博" , "url":"www.weibo.com" }
]`
console.log(typeof obj) //string
? console.log( typeof eval(obj))//object
12.2如果傳入的參數(shù)不是字符串直接返回這個參數(shù)
?let fn=function(x){
? ? ? ? ? ? console.log(x)
? ? ? ? }
? ? ? let i=1;
console.log(eval(i))//1
console.log(fn)//? (x){ console.log(x)}
12.3 關(guān)于eval()最重要的是,它使用了調(diào)用它的變量作用域環(huán)境,也就是,它查找的變量的值和新定義新變量和函數(shù)操作和局部作用域代碼一模一樣,如果定義了一個局部變量x,任何調(diào)用eval("x"),它會返回局部變量的值。如果他調(diào)用eval('x=1');它會改變局部變量的值。如果函數(shù)調(diào)用了eval('var y=3'),它聲明了新的局部變量,同樣的也可以聲明一個局部函數(shù) eval('function fn(){ return? x+1}');如果在頂層代碼調(diào)用eval(),它當(dāng)于全局變量和全局函數(shù)
注:傳遞給eval()的字符串必須在語法上講的同,比如eval('retuen;') 沒有任何意義,因為return只有在函數(shù)中起作用
12.4 全局的eval()
eval()具有更改局部變量的能力,這對于JavaScript優(yōu)化器來說是一個很大的問題。因為用于動態(tài)執(zhí)行的代碼通常來講是不能分析的,一般來講,如果一個函數(shù)調(diào)用了eval(),那么JavaScript解釋器將無法對這個函數(shù)做進一步優(yōu)化。按照這個邏輯,如果將eval()賦值給一個變量g,那么解釋器將無法優(yōu)化任何調(diào)用g()的函數(shù)
eg:
?let geval=eval;
? ? ? ? let x='global',y='global';
? ? ? ? function f(){
? ? ? ? ? ? var x='local'
? ? ? ? ? ? eval('x+="change"');
? ? ? ? ? ? return x;
? ? ? ? }
? ? ? ? function g(){
? ? ? ? ? ? var y='local';
? ? ? ? ? ? geval("y+='change'");
? ? ? ? ? ? return y;
? ? ? ? }
? ? ? ? console.log(f(),x);//localchange global
? ? ? ? console.log(g(),y);//local globalchange ? ? ?
12.5?在嚴(yán)格模式下,或者eval()執(zhí)行的代碼段以"use strict"指令開始,這里的eval()是局部eval(),并且不能再局部作用域中定義新的變量或函數(shù),但是可以查詢或者更改局部變量
13其他運算符
13.1 條件運算符
條件運算符是為一個的三元運算符(三個操作數(shù)),第一個操作數(shù)在?之前,第二個操作數(shù)在?和:直接,第三個操作數(shù)在冒號之后,第一個操作數(shù)是布爾值,如果是真值,那么計算第二個操作數(shù),并返回計算結(jié)果,否則計算第三個操作數(shù)返回計算結(jié)果
eg:
greeting='hello'+(username ? username : 'three');
等價于
? ? ? greeting='hello';
? ? ? if(username){
? ? ? ? ? greeting += username
? ? ? }else{
? ? ? ? ? greeting+='three'
? ? ? } ?
13.2 typeof運算符
typeof是一元運算符,放在單個操作數(shù)前面,操作數(shù)可以是任意值,返回值為操作數(shù)類型的字符串

注:typeof用來檢測原始值比較好,復(fù)雜的用instanceof檢測
13.3delete運算符
delete是一元運算符,它用來刪除對象的屬性或者數(shù)組的元素,delete是有副作用的,它是用來刪除操作,不是用來返回一個值
注:
1.當(dāng)刪除一個屬性時,這個屬性將不存在,訪問一個不存在的屬性將返回一個undefined,但是可以通過in運算符檢測是否在對象存在
2.當(dāng)刪除一個數(shù)組的元素時,這個元素刪除了,但是數(shù)組的長度沒變
3.delete操作數(shù)是一個左值,如果不是,將會返回一個true
4.在嚴(yán)格模式下,如果delete是非法的,比如變量,函數(shù),參數(shù),delete操作會拋出一個語法錯誤,只有操作數(shù)是一個屬性訪問表達式才可以正常工作
eg:
var o={x:1,y:2};
delete o.x;
"x" in o ;//false
var a=[1,2,4];delete a[2];?
2 in a ; //false
a.length//3? ??
delete 'sss';//true
13.4 void運算符
void是一元運算符,它出現(xiàn)在操作數(shù)之前,操作數(shù)可以是任意類型。這個運算符并不是經(jīng)常使用,操作數(shù)會照常計算,但忽略計算結(jié)果返回的undefined
這個運算符最常用在客戶端URL——javascript:URL中,在URL中可以寫帶有副作用的表達式,而void則讓瀏覽器不必顯示這個表達式的計算結(jié)果。例如,經(jīng)常在HTML代碼中的<a>標(biāo)簽里使用void運算符
? <a href="javascript:void window.open();">打開一個新窗口</a>
13.5 逗號運算符
逗號運算符是二元運算符,它的操作數(shù)可以任意類型。它首先計算左操作數(shù)值,但是忽略結(jié)果不計,然后計算右操作數(shù)值,最后返回右操作數(shù)值
var a = 20;
var b = (++a,10);
alert(b);//10
?let i=0,j=1,k=2;
等價于
let i=0;
let j=1;
let k=2;
for(var i=0,j=10;i<=j;i++,j--){
? ? console.log(i,j)
}

