JavaScript基础系列
复习上期知识点:数据类型
基本类型与引用类型:复制变量值
- 基本类型:会在变量对象上创建一个新值,然后把该值复制到新变量分配的位置上,此二者相互不影响
- 引用类型:当一个变量向另一个变量复制引用类型的值时,复制的是对象的引用,两个变量最终指向了同一个对象
# 基本类型复制
var num1 = 1;
var num2 = num1;
num2 = 2;
console.log(num1) // 1
# 引用类型复制
var obj1 = new Object();
var obj2 = obj1;
obj2.name = 'hello';
console.log(obj1.name) //"hello"
基本类型与引用类型:传递参数
ECMAScript 中所有函数的参数都是按值传递的,就是说和把值从一个变量复制到另一个变量一样。
- 基本类型:被传递的值会被复制给一个局部变量,此二者相互不影响
- 引用类型:是按值传递的,并不是按引用传递的
# 基本类型
function addTen(num) {
num += 10;
return num;
}
var count = 20;
var result = addTen(count);
console.log(count) // 20
# 引用类型
function setName(obj) {
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName(person);
console.log(person.name); //"Nicholas"
数据类型:隐式类型转换
# 1.数组先调用 valueOf(), 返回的是[],不是原始值
# 2.继续调用 toString(),返回空字符串,是原始值
# 3.进行原始值直接的类型转换
[] == 0 // true
# 1.数组先调用 valueOf(), 返回的是[],不是原始值
# 2.继续调用 toString(),返回空字符串,是原始值
# 3.进行字符串的链接操作
[] + 1 // "1",是字符串
# 1.数组先调用 toString(), 返回的是本地字符串,是原始值
# 3.进行字符串的链接操作
new Date() + 1 // "Sun Feb 02 2020 19:18:29 GMT+0800 (中国标准时间)1"
"+" 和 "==" 应用的对象(非日期类)到原始值的转换基本上都是对象到数字的转换(首先调用 valueOf());
"+" 和 "==" 应用的日期对象则使用对象到字符串的转换模式(首先调用 toString())。通过 valueOf() 或 toString() 返回的原始值被直接使用,而不会被强制转换为数字或字符串。
字符串与数字
对于数字和字符串操作符来说,加号运算符和比较运算符的行为有所不同。
- 加号运算符更偏爱字符串,如果一个操作数是字符串,则进行字符串连接
- 比较运算符更偏爱数字,如果一个操作数是数字,则进行数字的比较
表达式和运算符
表达式
表达式是 JavaScript 中的一个短语,JavaScript 解释器会将其计算出一个结果。
1.原始表达式
原始表达式是表达式的最小单位,它们不再包含其他表达式。原始表达式包含常量或直接量、关键字和变量。
1.23 // 数字直接量
hello // 字符串直接量
/pattern/ // 正则直接量
false // 保留字,关键字
true // 保留字,关键字
null // 保留字,关键字
i // 变量
undefined // 全局变量,与 null 不同,不是一个关键字
2.对象和数组的初始化表达式
是一个新创建的对象和数组,有时候称做对象直接量和数据直接量。
var a = [1, ,2 ,3] // 数组直接量
var obj = {x: 2, y: 3} // 对象直接量
3.函数定义表达式
函数定义表达式定义一个 JavaScript 函数。表达式的值是这个新定义的函数。从某种意义上讲,函数定义表达式可以称为 "函数直接量"。
var fun = function(x) { return x + 2}
4.属性访问表达式
属性访问表达式运算得到一个对象属性或一个数组元素的值。JavaScript 为属性访问定义了两种语法。
a.b //.identifier 法
a[0] // 方括号法
如果要访问的属性名是一个保留字或包含空格和标点符号、或是一个数字、或是通过运算得出的值而不是固定的值时,必须使用方括号写法。
5.调用表达式
JavaScript 中的调用表达式是一种调用函数或方法的语法表示。
f(1);
Math.max(x, y, z)
6.对象创建表达式
对象创建表达式创建一个对象并调用一个函数(这个函数称做构造函数)初始化新对象的属性。对象创建表达式和函数调用表达式方式非常类似,只是对象创建表达式之前多了一个 new 关键字。
new Object()
new Point(2, 3)
7.算术表达式
基本的算术运算符包括 *(乘法)、/(除法)、%(求余)、+(加法) 和 -(减法)。除 + 外,其他运算符会将操作数转换为数字,然后进行计算。
那些无法转换为数字的操作数会转化为 NaN 值,操作结果也是 NaN,重点学习 + 运算符。
加号算术运算符
加号的转换规则优先考虑字符串连接。从技术上讲,加法操作符的行为表现如下:
- 如果其中一个操作数是对象,则对象会遵循对象到原始值的转换规则转换为原始值类(日期对象调用 toString() 进行转换;其他对象通过 valueOf() 进行转换,对于不具备 valueOf() 方法,会调用 toString() 方进行转换)
- 进行了对象到原始值转换后,如果其中一个操作数是字符串,会进行字符串连接
- 否则,这个操作数都将转换为数字(或 NaN),进行加法操作
# 原始值之间操作
1 + 2 // 3,加法
# 原始值之间操作
1 + '2' // "12",连接操作
# 对象先调用 valueOf(),然后调用 toString()
# 因为 {} 转换为字符串,所以进行字符串操作
1 + {} // "1[object Object]"
# 原始值到数字的转换
true + true // 2,布尔值转换为数字后做加法
# 原始值的操作
2 + null // 2, null 转换为0后做加法
# 原始值的操作
2 + undefined // NaN,undefined 转换为 NaN 后做加法
需要特别注意是,当加号运算符与字符串和数字一起使用时,需要考虑加法的结合性对运算顺序的影响。
一元算术运算符
一元运算符是右结合,优先级比较高。
+一元加法运算符把操作数转换为数字(或 NaN),并返回这个转换后的数字-一元减法运算符会根据需要把操作数转换为数字,并改变运算结果的符号++递增运算符对操作数进行增量(加1)操作,会将操作数转换为数字,然后进行 +1 操作--递减运算符对操作数进行减量(减1)操作,会将操作数转换为数字,然后进行 -1 操作
增量和减量运算符分为前增(减)量和后增(减)量,前增(减)量会对操作数进行增(减)量计算,并返回计算后的值;后增(减)在对操作数进行增(减)之前,返回未做增(减)的值
var i = 1; j = ++i; // i = 2; j = 2;
var i = i; j = i++; // i = 2; j = 1
位运算符
位运算符可以对由数字表示的二进制数据进行更低层级的按位运算。位运算符要求它的操作数都是整数,且是32位的。
- &(按位与): 对它的整型操作数逐位进行布尔与(AND)操作,只有两个操作数中相对于的位都1,结果中的这一位就是1
- |(按位或): 对它的整型操作数逐位进行布尔或(OR)操作,只要两个操作数中相对于的位有一个是1,结果中的这一位就是1
- ^(按位异或): 对它的整型操作数逐位进行布尔异或(XOR)操作,只要两个操作数中相对于的位只能有一个是1,结果中的这一位才是1
- ~(按位非): 它将操作数的所有位取反
8.关系表达式
关系运算符用于测试两个值之间的关系,根据关系是否存在而返回 true 或 false。
严格相等运算符(===)
- 如果两个值类类型不相同,return false
- 如果其中一个值是 null,return false
- 如果其中一个值是 NaN,return false
- 如果两个值都是布尔值 true 或 false,return true
- 如果两个值都是数字且数值相等,return true
- 如果两个值都是字符串,长度和内容相等,return true
- 如果两个引用值指向同一个对象、数组或函数,return true,否则 return false
相等运算符(==)
- 如果两个操作数类型相同,比较规则与严格相同的比较规则一样
- 如果两个操作数类型不同,检测会遵循下面的规则和类型转换
- 如果一个值是 null,另一个值是 undefined,return true
- 如果一个值是数字,另一个是字符串,先将字符串转换为数字,然后进行值比较
- 如果一个值是boolean类型,先将其转换为数字,然后再进行比较
- 如果一个值是对象,另外一个值是数字或字符串,先将对象转换为原始值,然后再进行比较
# 先将 true 转换为数字 1
# 然后将 "1" 转换为数字 1
"1" == true // 结果为 true
比较运算符
只有数字和字符串的比较才是有意义的,其规则如下:
- 如果操作数是对象,将按照类型转换规则将其转换为原始值
- 在对象转换为原始值之后,如果两个操作数都是字符串,按照字母表的顺序进行比较
- 在对象转换为原始值之后,如果有一个操作数是数字,会将另外操作数转换为数字,进行比较
- NaN 与任意操作数比较,都返回 false
1 < NaN // false
1 > NaN // false
1 == NaN // false
对于数字和字符串操作符来说,加号运算符和比较运算符的行为有所不同。
- 加号运算符更偏爱字符串,如果一个操作数是字符串,则进行字符串连接
- 比较运算符更偏爱数字,如果一个操作数是数字,则进行数字的比较
instanceof 运算符
instanceof 运算符判断一个对象是否是一个类的实例。需要注意的是,所有的对象都是 Object 的实例。
当通过 instanceof 判断一个对象是否是一个类的实例的时候,也会包含对父类的检测。
9.逻辑表达式
逻辑运算符 &&、||、!是对操作数进行布尔算术运算,经常和关系运算符一起配合使用。
- && 有三个不同层次的理解
- 1.当操作都是布尔值,对其进行布尔与(AND)操作,结果返回布尔值
- 2.当操作数不是布尔值,会对真值和假值进行布尔与(AND)操作
- 如果两个操作数都是真值,返回一个真值
- 如果有一个假值,则返回一个假值
- 3.短路操作,会先计算做左操作数的值
- 如果是真值,表达式的结果依赖右操作数的值,否则就是假值
- 如果是假值,表达式结果一定是假值
var p = {x: 1, y: 2}
p && p.x // 1
10.赋值表达式
+=、-=、*=、&= 等等,先执行运算操作,然后再进行赋值操作。
11.表达式计算
JavaScript 通过全局函数 eval() 来完成这个工作(解释运行由 JavaScript 源代码组成的字符串)。
eval() 的问题在于,用于动态执行的代码通常来讲是不能分析的,如果一个函数调用了 eval(),那么解释器将无法对这个函数做进一步优化,而是将 eval() 定义为函数的另一个问题,他可以被赋予其他函数名。
var f = eval;
var g= f;
eval()
eval() 只有一个参数:
- 如果传入的参数不是字符串,它直接返回这个参数
- 如果参数是字符串,会将字符串当成 JavaScript 代码进行编译
关于 eval() 最重要的是,它使用了调用它的变量作用域环境。也就是说,它查找变量的值、和定义新变量与函数的操作和局部作用域中的代码完全一样。
eval('var i = 1;')
console.log(i) // i
eval("i = i + 2")
console.log(i) // 3
如果在对顶层代码中调用 eval(),它会作用于全局变量和全局函数。
全局 eval()
当脚本定义 eval() 的一个别名,且用另一个名称调用它,这个时候 eval() 会将其字符串当成顶层的全局代码来执行。折行的代码可能会定义新的全局变量和全局函数,或给全局变量赋值。
ECMAScript 5是反对使用 eval() 别名方式调用的,并规范了 eval() 的行为。
var geval = eval
var x = "global";
var y = "global";
function f() {
var x = "local";
eval("x += 'changed';");
return x;
}
function g() {
var y = "local";
geval("y += 'changed';");
return y;
}
console.log(f(), x); // localchanged global
console.log(g(), y); // local globalchanged
严格 eval()
严格模式下调用 eval() 时,或者 eval() 执行的代码段以 "use strict" 指令开始,这里的 eval() 是私有上下文环境中的局部 eval(),只能查询或更改局部变量,但不能在局部作用域中定义新的变量和函数。
12.其他运算符
- ?: 三元运算符
- typeof: 类型判断
- delete 运算符: 删除对象属性或数字元素
- void 运算符: 忽略计算结果并返回 undefined
- 逗号运算符: 首先计算左操作数,然后计算有操作,最后返回右操作数,常用于 for 循环中
# typeof
typeof undefined // undefined
typeof null // object
typeof false // boolean
typeof 1 // number
typeof NaN // number
typeof Infinity // number
typeof '1' // string
typeof (() => {}) // function
typeof {} // object
# delete
var p = {x: 1, y: 2}
delete p.x // true
p.x // undefined
x in p // false
delete p // false
# 逗号
i= 0, j = 1, k = 2 // 2
运算符概述
运算符 操作 结合性 操作数个数 操作数类型及运算结果类型
------------------------------------------------------------------------
++ 前/后增量 R 1 lval -> num
-- 前/后增量 R 1 lval ->num
- 求反操作 R 1 num -> num
+ 转换为数字 R 1 num -> num
~ 按位求反 R 1 int -> int
! 逻辑非 R 1 bool -> bool
delete 删除属性 R 1 lval -> bool
typeof 类型检查 R 1 any -> str
------------------------------------------------------------------------
*、/、% 乘除,求余 L 2 num, num -> num
------------------------------------------------------------------------
+、- 加减操作 L 2 num, num -> num
+ 字符串连接 L 2 str, str -> str
------------------------------------------------------------------------
<< 无符号左移位 L 2 int,int -> int
>> 有符号右移 L 2 int, int -> int
>>>>> 无符号右移 L 2 int, int -> int
------------------------------------------------------------------------
<、<= 比较操作 L 2 num(str), num(str) -> bool
>、>= 比较操作 L 2 num(str), num(str) -> bool
instanceof 对象判断 L 2 obj, func -> bool
in 属性存在 L 2 str, obj -> bool
------------------------------------------------------------------------
==、== 相等判断 L 2 any, any -> bool
!==、!= 不等判断 L 2 any, any -> bool
------------------------------------------------------------------------
& 按位与 L 2 int, int -> int
------------------------------------------------------------------------
^ 按位异或 L 2 int, int -> int
------------------------------------------------------------------------
| 按位或 L 2 int, int -> int
------------------------------------------------------------------------
&& 逻辑与 L 2 any, any -> any
------------------------------------------------------------------------
|| 逻辑或 L 2 any, any -> any
------------------------------------------------------------------------
?: 条件运算符 R 3 bool, any, any -> any
------------------------------------------------------------------------
= 赋值 R 2 lval, any -> any
*=、/=、%= 赋值 R 2 lval, any -> any
+=、-=、&= 赋值 R 2 lval, any -> any
------------------------------------------------------------------------
’ 忽略第一个操作数 L 2 any, any -> any
返回第二个操作数
------------------------------------------------------------------------
运算符的优先级顺序是从高到低排序的,每个水平分割线内的一组运算符具有相同的优先级;可以通过括号改变运算符的优先级。
L: 指从左到右结合,R: 指从右到左结合。