值的转换
什么是表达式?
表达式是js的一个短语,js解析器会将其计算出一个结果。
例如:程序中的常量是最简单的一种表达式,变量也是一种表达式,他的值就是赋值给变量的值。复杂表达式由简单表达式构成。比如数组访问表达式(是由表示一个数组的表达式、左方括号,整数,右方括号组成,eg:arr[0]);同样的函数调用表达式(是由一个表示函数对象的表达式和0个或者多个参数构成)
将简单的表达式组合成复杂的表达式最常用的方法是使用运算符。运算符根据特定的规则对操作数(通常两个)进行运算,并计算出新值。eg:x*y
表达式分为以下几种
1.原始表达式
1.1什么是原始表达式
最简单的表达式就是原始表达式,原始表达式是表达式中最小的单位—他们不包含其他表达式。原始表达式包含常量,变量,关键字,直接量
直接量是直接在程序中出现的常数值
eg:
'hello' //字符串直接量
12 //整型直接量
/pattern/ //正则表达式直接量
3.14 //浮点类型直接量
变量
变量是一个符号名称,可以通过名称获得对值的引,变量首字母必须由字母,下划线,$开始,后续的字符可以是字母、下划线、数字、$,也可以用非英语语言或者数学符号来书写标识符,变量可以赋予不同类型的值
eg:
i //返回变量i的值
sum //返回变量sum的值
undefined //undefined全局变量
关键字(js中的一些保留字构成了原始表达式)
eg:
true //返回一个布尔值 真
false //返回一个布尔值假
null //返回一个空值
this //返回当前对象
2.对象和数组初始化表达式
2.1什么是对象和数组初始化表达式?。
对象和数组初始化表达式实际上是一个新创建的对象和数组。这些初始化表达式成为‘对象直接量’和‘数组直接量’
2.2 数组初始化表达式
数组初始化表达式是通过一堆方括号和其内的逗号隔开的列表构成,数组初始化的表达式中的元素可以是任意类型的值,也可以数组套数组
eg
[ ] //一个空的数组
[1+2,2+3]//用于两个元素的数组
[[1,2,3],[4,5,6],{title:'标题1',num:2},null,undefined] //拥有5个元素的数组
注:数组直接量中的列表逗号之前的元素可以直接省略,这时省略的空位会填充undefined
let arr=[1,,,5]//这数组包含5个元素,其中三个是undefined
2.2 对象初始化表达式
对象初始化表达式和数组初始化表达式非常类似,只是方括号由花括号代替,并且每个子表达式都包含一个属性名和一个冒号作为前缀
eg:
let p={x:2.3,y:-1.2}
对象直接量也可以嵌套
let square={left:{x:2,y:3}}
3.函数定义表达式
函数定义表达式定义一个js函数,函数定义表达式可称为‘函数直接量’。一个典型的函数定义表达式包含关键字function,跟随其后是一对圆括号,括号内是一个以逗号隔开的列表,列表包含0个或者多个标识符(参数名),然后再跟随一个由花括号包裹的js代码段(函数体),函数定义表达式同样可以包含函数名字。函数也可以通过函数语句来定义,而不是函数表达式
eg:
var square = function(x) { return x*x}
4.属性访问表达式
属性访问表达式运算得到一个对象的属性或者一个元素的值。
js定义的两种语法
*第一种对象表达式后面紧跟一个句点和标识符
let o={x:1,y:{z:3}}
let arr=[o,4,[5,6]]
o.x //o的x属性的值
o.y.z //o.y的属性z的值
*第二种写法是使用方括号,方括号内是另外一种表达式(这种方法适用于对象和数组)。第二个表达式指定要访问的属性名称或者代表要访问的数组元素的索引
eg:
o["x"]//o的x属性的值
arr[1] //表达式arr中索引值为1的元素
arr[0].x //达式arr中索引值为0的元素的x属性
注:如果对象表达式跟随一对方括号,则会计算方括号内的表达式的值并将它转为字符串,如果命名的属性不存,那么整个属性表达式的值是undefined
5.调用表达式
调用表达式是一种调用(或执行)函数或方法的语法表示。
它是一个函数表达式开始,这个函数表达式指定了要调用的函数。函数表达式后跟随一个圆括号,括号内是一个以逗号隔开的参数列表,参数可以是0个也可以是多个
eg:
f(0);
Math.max(x,y,z);
a.sort();
当对调用表达式进行求值时,首先计算函数表达式,然后计算参数表达式,得到一组参数值。然后实参的值依次赋给给形参,这些形参是定义函数时指定的,接下来开始执行函数体。如果函数使用return语句给出一个返回值,那么这个值就是函数调用表达式的值。否则就是undefined。 如果函数表达式的值不是一个可调用的对象,则返回语法错误(所有函数默认返回值都是未定义)
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:
如果这个表达式是一个属性访问的表达式,这个调用成为方法调用,方法内的this指向其宿主对象
eg:
let person={
name:'jiaojiao',
say:function(){
console.log(this.name)
}
}
person.say();//jiaojiao 这里的this指向person
如果是全局函数,this指向window,在严格模式下this是undefined
eg:
function func() {
console.log(this)//window
}
func();
function func() {
"use strict";
console.log(this)//undefined
}
func();
6.对象创建表达式
对象创建表达式创建一个对象创建一个对象并调用一个函数(这个函数被称为构造函数)初始化新对象的属性。对象创建表达式和函数表达式非常类似,只是对象创建表达式之前多了一个关键字new
new Object()
new Proint(2,3)
如果一个对象创建表达式不需要传入参数给构造函数的话,那么这对空圆括号是可以省略的
new Object
new Date
当计算一个对象创建表达式时,和对象初始化化通过{}创建对象的做法一样,js首先创建一个新的空的对象,然后,js通过传入指定参数并将这个新对象当做this的值调用一个指定函数。这个函数可以使用this来初始化这个新创建的对象的属性。那些被当做构造函数的函数不会返回一个值,并且这个新创建并初始化后的对象就是整个对象创建表达式的值。如果一个构造函数确实返回了一个对象值,那么这个对象就作为整个对象创建表达式的值,而新创建的对象就废弃了
7.运算符的概述
js中的运算符用于算术表达式,比较表达式,赋值表达式等。下面表格列出了js的运算符,作为一分方便的参照
注:大多数的运算符由标点符号表示的。另外一些由关键字表示的,比如delete和instanceof
下面的表格是按照运算符的优先级排序的,前面的运算符优先级要高于后面的运算符优先级。被水平线隔开的运算符具有不同的优先级,标题A列表示运算符的结合性,L(从左至右),R(从右至左),标题N表示操作数的个数。标题类型表示期望操作的类型,以及运算符结果类型
注:lval左值
7.1操作数的个数
运算符可以根据其操作数的个数进行分类,一个操作数是一元运算符,两个操作数的是二元运算符,三个操作数的是三元运算符
一元运算符
三元运算符
其他的都是二元运算符
7.2操作数类型和结果类型
一些运算符可以作用于任何类型,但仍然希望它们的操作数是指定类型的数据,并且大多数运算符返回一个特定类型的值。如表运算符标题为‘类型’的列列出了运算符操作数的类型(箭头前)和运算结果的类型(箭头后)。
eg: console.log("3"*"5");//15
"3"*"5" 表达式的值是数字15,是因为乘法运算符“*”希望操作数是数字,js将操作数转换为数字了
eg: console.log(1+"11");//111
1+"11"表达式的值是字符串111,js将操作数1转换为字符串‘1’
还有很多类型的转换,后面会充分解释在什么情况下回转换
7.3 左值
左值是指"表达式只能出现在运算符的左侧”,在js中,变量,对象属性和数组元素均是左值。在es规范允许内置函数返回一个左值,但是自定义函数不能返回左值
7.4 运算符的副作用
计算一个简单的表达式(比如2*3)不会对程序的运行状态造成任何影响,程序后续执行的计算也不会受到该计算的影响。而有一些表达式有很多的副作用,前后表达式运算会相互影响。赋值运算符是最明显的一个例子:如果给一个变量赋值或属性赋值,那么使用这个变量或属性的表达式的值会发生改变。比如‘++’和‘--’,因为他们包含隐式的赋值。delete运算符同样有副作用,删除一个属性就像(但是不完全一样)给这个属性赋值undefined
其他的js运算符都没有副作用,但函数调用表达式和对象创建表达式有些特别,在函数体或者构造函数内部运用可这些运算符并产生副作用的时候,我们说函数掉用表达式和对象创建表达式是有副作用的
7.5 运算符优先级
1.表7-1中运算符的优先级是从高到低,。每个水平分割线内的一组运算符具有相同的优先级。运算符的优先级控制这运算符的执行顺序。优先级高的要高于优先级低的
eg:看下面的例子,因为乘法运算符(*)高于加法运算符+(),赋值运算符“=”是最低的,所以先执行y*z,然后执行加法+x,最后执行赋值
w = x+y*z
2.运算符的优先级可以通过圆括号来重写。为了让加法先执行,我们可以这样写
w=(x+y)*z
3.属性访问表达式和调用表达式的优先级比任何运算符的优先级都搞。eg:
typeof my.functions[x](y);
尽管typeof是优先级最高的运算符之一,但是typeof 也是在两次属性访问和函数调用后执行
注:实际上,如果你真的不确定你所使用的运算符的优先级,最简单的方法用圆括号强行指定运算次序。有些重要规则需要熟记,乘法和除法的优先级高于加法和减法,赋值运算符的优先级非常低,通常最后执行
7.6 运算符的结合性
在“表7-1”标题为A的列说明的运算符的结合性。L是从左到右结合(从左到右的顺序实现),R是从右到左结合(从右想左的顺序执行),结合性指定了在多个具有同样优先级的运算符表达式的运算顺序。
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 ))
因为一元运算符,赋值和三元条件运算符都具有从右向左的结合性
7.7运算顺序
运算符的优先级和结合性规定了他们在复杂的表达式的运算顺序,但是没有规定子表达式的计算过程顺序。js总是严格按照从左至右的顺序来计算表达式,eg:w=x+y*z中,先计算子表达式w,然后计算x,y,z的值,然后在计算y*z ,再加上x的值,最后将其赋值给表达式w所指代的变量和属性。给表达式添加圆括号会改变乘法,加法个赋值运算的关系,但是从左到的顺序是不会变得。
只有在任何一个表达式具有副作用影响到其他表达式时候,其求值顺序才会看上有所不同,如果表达式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.把结果赋值给b
8算术表达式
本节涵盖了那些进行算术计算的运算符,以及对操作数的算术操作。乘法,除法和减法运算符非常简单,我们首先讲解它们。加法运算符单独占一节,因为加法运算符可以做字符串的连接操作,并且类型转换有些特殊。一元运算符和位运算符同样在单独的两节中讲到
基本运算符*(乘法),/(除法),%(求余),-(减法)
这四个运算符很简单,会把非数字的操作数转换为数字,然后求积,商,余数,差。那些无法转为数字的操作数转为NaN,如果操作数(或者转换结果)是NaN,算术的运算结果也是NaN , 0/0也是NaN。任何数除以0运算结果是父无穷大或者正无穷大
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 '+'运算符
二元加法运算符“+”可以对两个数字做加法,也可以做字符串连接操作
1.当两个操作数都是数字时做加法运算,两个都是字符串时做字符串连接
console.log(1+2);//3
console.log('hello'+' jiaojiao');// hello jiaojiao
console.log('1'+'2');//12
加法运算符优先考虑字符串的连接,如果其中一个操作符是字符串或者转换为字符串的对象,则另外一个操作数会转为字符串,加法将进行字符串的连接
注:其中一个操作数是对象,则对象会遵循对象到原始值的转换规则转换为原始值,日期对象通过toString()方法进行转换,其他的对象则通过valueOf()的方法进行转换。如果不是原始值,再通过toString()转换,
在进行了对象带原始值的转换后,如果其中一个操作数是字符串,另一个操作数也会转为字符串,然后进行字符串连接
否则,两个操作数都会转为数字或者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
注:需要特别注意的是,当加法运算符和字符串一起使用时,需要考虑加法的结合性的对运算顺序的影响。也就是说,运算结果是医院与运算符的运算顺序,例如以下案例,第一行没有圆括号,“+”运算符具有从左到右的结合性,因此两个数字先进行加法运算,运算结果和字符串进行连接。第二行中,圆括号改变了运算顺序,数字2和字符串连接,生成一个新的字符串,然后数字1再跟这个新的字符串进行连接,生成最终的结果
console.log(1+2+" hello jiaojiao") //3 hello jiaojiao
console.log(1+(2+" hello jiaojiao"));//12 hello jiaojiao
8.2 一元算术运算符
一元算术运算符作用于一个单独的操作数,并产生一个新的值。在js中,一元运算符有很高的优先级,都是右结合
+,-是一元运算符,也是二元运算符
8.2.1 一元加法运算符(+)
一元加法运算符把操作数转换为数字(或者NaN) , 并返回这个转换后的数字。如果操作数本身就是数字,则直接返回这个数字
8.2.2 一元减法运算符(-)
当“-” 用作一元运算符时,它会根据需要把操作数转换为数字,然后改变运算结果的符号
8.2.3 递增(++)运算符
递增(++)运算符对其操作数进行加一操作,操作数是一个左值(变量,数组元素或者的对象属性),运算符将操作数转换为数字,然后数字加一,并将加一后的数值重新赋值给变量,数组元素或者对象属性
递增“++”运算符的返回值依赖于它操作数的位置。
1.当运算符在操作数之前,称为“前增量”运算符,它对操作数进行增量计算,并返回计算后的值
2.当运算符在操作数之后,称为“后增量”运算符,他对操作数进行增量计算,并未返回 计算后的值
两者的区别在于 eg:
let i=1,j=++i;//i=2,j=2
let i=1,j=i++; // i=2,j=1
需要注意的是,表达式++x并不综合x=x+1完全一样,如一下案例,因为++运算符会把操作数转为数字
eg:
let x="1"
x=x+"1" //x为 字符串11
++x;//x为2
8.2.4 递减(--)运算符
递减(--)运算符的操作数也是一个左值。它把操作数转换为数字,然后减一,并将计算的值重新赋值。和递增运算符一样,递减运算符的返回值依赖于他对操作数的位置,当递减(--)运算符在操作数之前,操作数减一,并返回减一之后的值,当递减(--)运算符在操作数之后,操作数减一,并未返回减一之后的值
9.关系表达式
关系运算符用于测试两个值之前的关系比如(‘等于’,‘小于’,‘是...属性’),根据关系是否存在而返回true或false。关系表达式总是返回一个布尔值,通常在if,while或者for语句中使用关系表达式,用以控制程序的执行流程。
9.1相等和不等运算符
9.1.1 严格相等“===”运算符
严格相等“===”运算符也叫恒等运算符,用来比较两个值是否严格相等,在比较的过程中没有任何类型的转变,来检测两个操作数的值和类型是否相等,如果操作数相等返回true,否则返回false,
eg:
● 如果两个值的类型不同,则它们不相等
console.log("1" === 1) //false
● 如果一个值是null,一个是undefined ,则它们不相等
console.log(null === undefined) //false
● 如果其中一个值是NaN,或者两个值都是NaN,它们不相等,NaN和任何值不相等,包括它本身
console.log(NaN === NaN) //false
● 如果两个值为数字且数值相等,则它们相等。如果一个值为0,另一个值为-0,它们同样相等
console.log(1===1.0)//true
console.log(0 === -0)//true
●如果两个值为字符串,如果它们的长度和内容相等,则它们相等
●如果两个引用值指向同一个对象,数组或者函数,则它们相等。如果指向不同的对象,则它们是不等,尽管两个对象具有完全一样的属性
9.1.3 相等“===”运算符
相等“==”运算符,用来比较两个值是否相等,相等则返回true,不等则返回false,这里的“相等”定义非常宽松,可以允许值的转换
●如果两个操作数类型相同,则和严格相等的比较规则一样,如果严格相等,那么比较结果相等,如果不严格相等,则比较结果为不相等
●如果两个操作数类型不相同,‘==’相等操作符也可能认为它们相等。检测相等会遵循如下规则进行转换
---如果一个值是null,另一个是undefined,则它们相等
---如果一个值是数字,另一个是字符串,先将字符串转换为数字,然后使用转换的值进行比较
---如果其中一个值是true,则将其转换为1再进行比较。如果其中一个值是false,则将其转换为0再进行比较
---如果一个是对象,一个是数字或者字符串,将对象转为原始值进行比较。对象通过toString()方法或者valueOf()转为原始值
---其他类型的比较则不相等
9.1.4 不相等“!=”运算符
不相等“!=”运算符检测规则是相等‘==’运算符结果取反
9.1.5 不全等相等“!==”运算符
不全等相等“!==”运算符检测规则是全等‘===’运算符结果取反
9.2比较运算符
比较运算符用来检测两个操作数的大小关系(数值的大小或者字母表的大小)
● 小于(<)
如果第一个操作数小于第二个操作数,则“<”运算符的运算结果为true,否则为false
●大于(>)
如果第一个操作数大于第二个操作数,则“>”运算符的运算结果为true,否则为false
●小于等于(<=)
如果第一个操作数小于等于第二个操作数,则“<=”运算符的运算结果为true,否则为false
●大于等于(>=)
如果第一个操作数大于等于第二个操作数,则“>=”运算符的运算结果为true,否则为false
比较运算符的操作数可能是任意类型。然而,只有数字和字符串才能真正执行比较操作,因此那些不是数字和字符串类型的操作数,将进行类型转换,转换规则如下
●如果操作数为对象,那么对象转换为原始值进行比较 ,如果vlaueOf()返回一个原始值,那么直接使用这个原始值。否则,使用toString()的转换结果进行比较操作
●在对象转为原始值之后,如果两个操作数都是字符串,那么将依照字母表的顺序对两个字符串进行比较,这里提到的‘字母表顺序’是指组成这个字符串的16位Unicode字符的索引顺序
●在对象转换为原始值之后,如果至少有一个操作数不是字符串,那么两个操作数转为数字进行比较
注:0和-0相等,Infinity比其他任何数字都大(除了本身),-Infinity比其他任何数字都小(除了它本身)。如果其中一个操作数转换为NaN,那么比较操作符总是转为false
●如果两个操作数,一个是数字,一个是字符串,比较运算符会先将字符串转为数字再进行比较
9.3 in运算符
in操作符用来检测in操作符用来判断某个属性属于某个对象,对于数组属性需要指定数字形式的索引值来表示数组的属性名称
in运算符希望它的左操作符是一个字符串或可以转为字符串,右侧的操作数是一个对象。如果右侧对象拥有一个名为左操作数值的属性名,那么表达式返回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 数字转为字符串
'3' in data //false
9.4 instanceof 运算符
instanceof是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型
它的左操作数是一个对象,右操作数是一个类,如果左操作数是右操作数的实例则返回true,否则返回false
let d=new Date();
console.log( d instanceof Date) ;//true d是由Date创建的
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 所有的数组都是对象
注:所有的对象都是Object实例
为了了解instanceof运算符是如何工作的,首先必须了解原型链,原型链作为js的继承机制,为了计算 o instanceof f ,js首先计算f.prototype,然后再原型链查找o,如果找到o是f(或者f的父类)的一个实例,表达式返回true,否则返回false
10.逻辑表达式
逻辑运算符“&&”,“II”和“!”是对操作数进行布尔值的算术运算,经常和关系运算符一起配合使用,逻辑运算符将多个关系表达式组合起来组合成为一个更复杂的表达式
10.1逻辑与(&&)
"&&"运算符可以从3个不停层次进行理解
1.当操作数都是布尔值时,"&&"对两个布尔值进行and操作,只有两个值都为true的时候,它才返回true,如果其中一个值是false,它返回false
x==0 && y==0 //只有在x和y都为true的时候,才返回true
2.当操作数不是布尔值的时候,有些值可以是真值,有些值是假值
假值 false、null、0、-0、""、undefined 和 NaN,其他为真值
如果两个操作数都是真值,则返回一个真值,否则只是有一个操作数是假值,则返回假值
3.上文中我们提到运算符会返回一个真值或者假值,但是没有说这个真值或者假值到底是什么值?
&&运算符首先计算左侧的值,如果左侧值为假值,则直接返回左侧值,则不会计算右侧的值
如果左侧的值是真值,会计算表达式右侧的值,如果右侧的值是真值,则整个表达式的值一定是真值,否则整个表达式的值是假值
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逻辑或(||)
"||"运算符对两个操作数做布尔值或运算。如果其中一个或者两个操作数是真值,它返回一个真值,如果两个操作数都是假值,则返回一个假值。和(&&)运算符一样,首先会计算左侧的表达式,如果计算结果为真值,直接返回真值,不会计算右侧,如果为假值,才会计算右侧,右侧是真值,则返回真值,是假值返回假值,例如一些案例
/*如果max_width定义 ,max是max_width,如果max_width每天定义,preferences.max_width定义了max是preferences.max_width,否则max的值是500*/
let max=max_width || preferences.max_width || 500;
这种惯用法通常用于函数体内,用来提供默认值
function copy(o,p){
p= p || {};//如果p没有传入人格对象,则使用一个新建对象
}
10.3逻辑非
"!"运算符是一元运算符。它放置在一个单独的操作数之前。它的目的是将操作数的布尔值求反。例如,如果x是真值,则!x返回false;如果x是价值则!x返回true
"!"逻辑非首先将其操作数转换为布尔值,任何对布尔值取反
11.赋值表达式
"=" 运算符来给变量或者属性赋值
"=" 运算符 它的左侧操作数是一个变量或者对象属性(或者数组的元素)。它的右侧操作数可以是任意值。赋值表达式的值就是右侧操作数的值。
赋值表达式的副作用是,有操作数的值赋值给左侧的变量或对象属性,这样的话,后续对这个变量和对象属性的引用都将得到这个值,这样的话,后续对这个变量和对象属性的引用都将得到这个值
赋值操作符的结合性是从右向左
i=j=k=0 这个表达式等于 i=(j=(k=0))
带操作的赋值运算
除了常规的赋值运算符,js还支持其他的赋值运算符。eg,"+="执行的是加法运算和赋值操作
total+=sales_tax
等价于
total=tatal+sales_tax
运算符"+="可以用于数字或者字符串,如果其操作数是数字,它将执行加法运算和赋值操作,如果是字符串,它执行字符串的连接
这类运算符还包括“-=”,“*=”,“+=”,"/=","%="
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可以通过函数eval()来解析运行一个js源码组成的字符串,并返回一个值
eval()是一个函数,但是它已经当做运算符来对待了
eval()只有一个值。
12.1如果传入的参数是字符串,它会将字符串当做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如果传入的参数不是字符串直接返回这个参数
let fn=function(x){
console.log(x)
}
let i=1;
console.log(eval(i))//1
console.log(fn)//ƒ (x){ console.log(x)}
12.3 关于eval()最重要的是,它使用了调用它的变量作用域环境,也就是,它查找的变量的值和新定义新变量和函数操作和局部作用域代码一模一样,如果定义了一个局部变量x,任何调用eval("x"),它会返回局部变量的值。如果他调用eval('x=1');它会改变局部变量的值。如果函数调用了eval('var y=3'),它声明了新的局部变量,同样的也可以声明一个局部函数 eval('function fn(){ return x+1}');如果在顶层代码调用eval(),它当于全局变量和全局函数
注:传递给eval()的字符串必须在语法上讲的同,比如eval('retuen;') 没有任何意义,因为return只有在函数中起作用
12.4 全局的eval()
eval()具有更改局部变量的能力,这对于JavaScript优化器来说是一个很大的问题。因为用于动态执行的代码通常来讲是不能分析的,一般来讲,如果一个函数调用了eval(),那么JavaScript解释器将无法对这个函数做进一步优化。按照这个逻辑,如果将eval()赋值给一个变量g,那么解释器将无法优化任何调用g()的函数
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 在严格模式下,或者eval()执行的代码段以"use strict"指令开始,这里的eval()是局部eval(),并且不能再局部作用域中定义新的变量或函数,但是可以查询或者更改局部变量
13其他运算符
13.1 条件运算符
条件运算符是为一个的三元运算符(三个操作数),第一个操作数在?之前,第二个操作数在?和:直接,第三个操作数在冒号之后,第一个操作数是布尔值,如果是真值,那么计算第二个操作数,并返回计算结果,否则计算第三个操作数返回计算结果
eg:
greeting='hello'+(username ? username : 'three');
等价于
greeting='hello';
if(username){
greeting += username
}else{
greeting+='three'
}
13.2 typeof运算符
typeof是一元运算符,放在单个操作数前面,操作数可以是任意值,返回值为操作数类型的字符串
注:typeof用来检测原始值比较好,复杂的用instanceof检测
13.3delete运算符
delete是一元运算符,它用来删除对象的属性或者数组的元素,delete是有副作用的,它是用来删除操作,不是用来返回一个值
注:
1.当删除一个属性时,这个属性将不存在,访问一个不存在的属性将返回一个undefined,但是可以通过in运算符检测是否在对象存在
2.当删除一个数组的元素时,这个元素删除了,但是数组的长度没变
3.delete操作数是一个左值,如果不是,将会返回一个true
4.在严格模式下,如果delete是非法的,比如变量,函数,参数,delete操作会抛出一个语法错误,只有操作数是一个属性访问表达式才可以正常工作
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是一元运算符,它出现在操作数之前,操作数可以是任意类型。这个运算符并不是经常使用,操作数会照常计算,但忽略计算结果返回的undefined
这个运算符最常用在客户端URL——javascript:URL中,在URL中可以写带有副作用的表达式,而void则让浏览器不必显示这个表达式的计算结果。例如,经常在HTML代码中的<a>标签里使用void运算符
<a href="javascript:void window.open();">打开一个新窗口</a>
13.5 逗号运算符
逗号运算符是二元运算符,它的操作数可以任意类型。它首先计算左操作数值,但是忽略结果不计,然后计算右操作数值,最后返回右操作数值
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)
}