一个表达式是对一个变量或值的引用,或者是一组值和变量与运算符的组合。它返回一个值
表达式就是短语,js 解释器会计算表达式的结果<br />简单表达式 + 运算符 = 复杂表达式
每一个合法的表达式都能计算成某个值,但从概念上讲,有两种类型的表达式:有副作用的(比如赋值)和单纯计算求值的。
主要表达式
Primary Expression 是表达式的原子项。它是表达式的最小单位,它所涉及的语法结构也是优先级最高的。
this关键字
var obj = {
x:1,
f() {
console.log(this.x)
}
}
obj.f(); // 1
变量
字面量
数组和对象的初始化表达式
[1,,2]
{a:1}
函数定义表达式
var func = function(){} // 匿名
var func1 = function func1() {} 命名
(class {})
class a {}
var asyncfunc = function * () {}
async function () {}
async function * () {}
yield
yield *
await
字面量正则表达式
/fsf/g
模板字符串
`sdfsdf`
分组操作符
任何表达式加上圆括号,都被认为是Primary Expression,这个机制使得圆括号成为改变运算优先顺序的手段。
var a = 1;
var b = 2;
var c = 3;
// 默认优先级
a + b * c // 7
// 默认是这样计算的
a + (b * c) // 7
// 现在使加法优先于乘法
(a + b) * c // 9
左值表达式
属性访问表达式
var obj = {a:1}
obj.a
obj['a']
new 操作符
new Object()
new Point(2,3)
元属性
new.target属性
new.target属性允许你检测函数或构造方法是否是通过new运算符被调用的。
new.target指向了 new 调用的构造函数
在 arrow functions 中,new.target 指向最近的外层函数的new.target
- 函数调用中的 new.target ```javascript function Person(name) { if (new.target !== undefined) { this.name = name; } else { throw new Error(‘必须使用 new 命令生成实例’); } }
// 另一种写法 function Person(name) { if (new.target === Person) { this.name = name; } else { throw new Error(‘必须使用 new 命令生成实例’); } }
var person = new Person(‘张三’); // 正确 var notAPerson = Person.call(person, ‘张三’); // 报错
- 构造方法中的 new.target,子类继承父类时,`new.target`会返回子类。
```javascript
class Rectangle {
constructor(length, width) {
console.log(new.target === Rectangle);
// ...
}
}
class Square extends Rectangle {
constructor(length, width) {
super(length, width);
}
}
var obj = new Square(3); // 输出 false
利用这个特点,可以写出不能独立使用、必须继承后才能使用的类。
class Shape {
constructor() {
if (new.target === Shape) {
throw new Error('本类不能实例化');
}
}
}
class Rectangle extends Shape {
constructor(length, width) {
super();
// ...
}
}
var x = new Shape(); // 报错
var y = new Rectangle(3, 4); // 正确
import.meta
import.meta是一个给JavaScript模块暴露特定上下文的元数据属性的对象。它包含了这个模块的信息,比如说这个模块的URL。
实例
这里有一个 my-module.mjs模块
<script type="module" src="my-module.mjs"></script>
你可以通过 import.meta 对象获取这个模块的元数据信息.
console.log(import.meta); // { url: "file:///home/user/my-module.mjs" }
函数调用表达式
f(0)
obj.f();
super
super关键字用于访问和调用一个对象的父对象上的函数。
import 调用
标签模板
d = 7 //这个表达式返回7
a = b = c = d
a b c d // 都是7
参数列表
- () 返回空 List
- …arguments
- a,b,c
- a,…arguments
自增和自减
一元运算符. 将操作数的值加一. 如果放在操作数前面 (x), 则返回加一后的值; 如果放在操作数后面 (x), 则返回操作数原值,然后再将操作数加一.
var x=3;
console.log(++x); //4
console.log(x); //4
var y=3;
console.log(y++); //3
console.log(y); //4
一元操作符
delete
typeof
typeof 用来判断原始数据很有效果,但是判断 Object 就不行,使用 instanceof 判断引用类型
typeof '12'; // returns "string"
typeof `sdf` // returns 'string'
// NaN是一种特殊的number。
typeof 12; // returns "number"
typeof NaN; // returns 'number'
typeof dontExist; // returns "undefined"
typeof undefined // returns 'undefined'
typeof true; // returns "boolean"
typeof {}; // returns "object"
typeof /sdf/ // returns object
typeof null => object // Null 是特殊的对象,在 js 运行时里面的类型是 Null
ECMA-262 规定任何在内部实现[[Call]]方法 的对象都应该在应用 typeof 操作符时返回”function”。
typeof function (){}; // returns "function" 因为函数对象实现了[[call]]方法
var a1; //a1的值为undefined
var a2 = null;
var a3 = NaN;
alert(a1 == a2); //显示"true"
alert(a1 != a2); //显示"false"
alert(a1 == a3); //显示"false"
alert(a1 != a3); //显示"true"
alert(a2 == a3); //显示"false"
alert(a2 != a3); //显示"true"
alert(a3 == a3); //显示"false"
alert(a3 != a3); //显示”true"
从上面的代码可以得出结论:(1)undefined与null是相等;(2)NaN与任何值都不相等,与自己也不相等。
为了避免收到undefined,可以对参数进行检查
function abs(x) {
if (typeof x !== 'number') {
throw 'Not a number';
}
if (x >= 0) {
return x;
} else {
return -x;
}
}
毫无疑问,我们应该按照 typeof 的结果去理解语言的类型系统。但JS之父本人也在多个场合表示过,typeof 的设计是有缺陷的,只是现在已经错过了修正它的时机。
void
关于void运算符,有两种写法:
- void expression
- void (expression)
void运算符的操作数可以是任意类型。该运算符指定要计算一个表达式但是不论该表达式原来是否有自己的返回值,其返回值都为undefined。其作用如下:
- 作用一:返回undefined,(对于为什么不直接使用undefined,是因为undefined不是关键字,意味着它随时可能被篡改成其他值。。。)。
- 作用二:防止不必要的行为,如下代码:
如下创建了一个超链接文本,当用户单击该文本时,不会有任何效果。
<a href="javascript:void(0)">Click here to do nothing</a>
function func(){
return this;
}
console.log(void func());
-
一元减运算符将操作转换为Number类型并取反.
var x = 3;
-true // -1
y = -x; // y = -3, x = 3
+
一元加运算符将操作转换为Number类型.
console.log( +'3' ); // 3
console.log( '3' ); // '3'
console.log(+true); // 1
~
按位非运算符.
对每一个比特位执行非(NOT)操作。NOT a 结果为 a 的反转(即反码)。
对任一数值 x 进行按位非操作的结果为 -(x + 1)。例如,~5 结果为 -6。
源码求补码
正数: 和源码相同,+9的补码是00001001
负数:-9 按位取反加1,-9的补码11110111
补码求源码
符号为是0: 和源码相同。00001001是9
符号为是1:符号位不变,其余位取反后加1,11110111->10001001->-9
9 (base 10) = 00000000000000000000000000001001 (base 2)
~9 (base 10) = 11111111111111111111111111110110 (base 2) = -10 (base 10)
~9 所有位取反得到11111111111111111111111111110110,最高位符号位为1,表明是负数,100...01010,是-10
var str = 'rawr';
var searchFor = 'a';
// 这是 if (-1*str.indexOf('a') <= 0) 条件判断的另一种方法
if (~str.indexOf(searchFor)) {
// searchFor 包含在字符串中
} else {
// searchFor 不包含在字符串中
}
// (~str.indexOf(searchFor))的返回值
// r == -1
// a == -2
// w == -3
!
算术运算符
+ - * / % **
幂运算符**:
2 ** 3 // 8
10 ** -1 // 0.1
关系操作符
> < >= <=
in
instanceof
相等运算符
== != === !==
很多实践中推荐禁止使用“ ”,而要求程序员进行显式地类型转换后,用 === 比较。
根据JS的语法,要满足=的条件如下
- 如果是引用类型,则两个变量必须指向同一个对象(同一个地址);
- 如果是基本类型,则两个变量除了类型必须相同外,值还必须相等。
位移运算符
<<
按位左移运算符。a << b
// 将a的二进制串向左移动b位,右边移入0.
a << 1 // 8
>>
按位右移运算符。a >> b
// 把a的二进制表示向右移动b位,丢弃被移出的所有位.
// 左边多出的空位由原值的最左边数字补齐.注意会先转成32bit
a >> 1 // 2
>>>
按位无符号右移运算符。a >>> b
// 把a的二进制表示向右移动b位,丢弃被移出的所有位,并把左边空出的位都填充为0
二进制位运算符
操作数被转换为32bit整數,以位序列(0和1组成)表示.若超過32bits,則取低位32bit
- &(与)
a = 0b100
b = 0b001
a & b // 0 - |(或)
a | b // 5 - ^(位异)
每一个对应的位,两个不相同则返回1,相同则返回0
a ^ b // 5 - ~
var a = 4
~a // -5
逻辑运算
&& || !
在js逻辑运算中,0、””、null、false、undefined、NaN都会判为false,其他都为true
作为”&&”和”||”操作符的操作数表达式,这些表达式在进行求值时,只要最终的结果已经可以确定是真或假,求值过程便告终止
短路运算符
A && (A = B)相当 A 是true,返回 B,**A是 false,返回 A**短路
A = A || B相当 **A 是true,返回 A**短路,A是 false,返回 B
param && (this.param = param)
//用来判空
foo = foo || bar //如果foo存在,值不变,否则把bar的值赋给foo
function log(x, y) {
y = y || 'World';
console.log(x, y);
}
用来指定默认值
问题:y不能赋判false的值
在javascript的逻辑运算中,0、””、null、false、undefined、NaN都会判定为false,而其他都为true
var width = 100;
var height = 200;
var data = width || height ;
//data = 100
var width;
var height = 200;
var data = width || height ;
//data = 200
var width = 100;
var height = 200;
var data = height || width ;
//data = 200
条件(三元)运算符
(condition ? ifTrue : ifFalse)
逗号操作符
var i = 0, j = 9;
赋值运算符
复合赋值
这样的连续赋值,是右结合的,它等价于下面这种:
a = (b = (c = d)) //可以看出c = d表达式返回7
赋值表达式的使用,还可以结合一些运算符,例如:
x = y
x += y // x = x + y
x -= y // x = x - y
x **= y // x = x ** y
x |= y // x = x | y
// 全部
= *= /= %= += -= <<= >>= >>>= &= ^= |=
解构赋值
Destructuring定义
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,本质是赋值
左边一种结构,[],{}
右边一种可遍历的结构,进行解构赋值
以前的做法,为变量赋值,只能直接指定值。
let arr = [1,2]
let first = arr[0]
数组解构赋值
如果左右是数据,就叫数组解构赋值
根据顺序去匹配
let [a, b, c] = [1, 2, 3];
let a,b,c,rest;
// 跳过不赋值的变量
[a,,b] = [1,2,3] // a = 1, b = 3
// 带默认
[a,b,c=3] = [1,2]; //a = 1, b = 2,c=3
[a,b] = '123' // a = 1, b = 2 只要是可遍历的
// 剩余
[a,b,…rest]=[1,2,3,4,5,6]; //a = 1, b = 2, rest = [3,4,5,6];
[a,b] = [b,a]; // a = 2, b = 1,变量交换
对象解构赋值
如果左右是对象,就叫对象解构赋值,包括字符串(左边是数组,右边是字符串),布尔值,,数值解构赋值
根据key,value去匹配
var obj = {k:1,a:1,b:1,d:1,e:5};
var {a,b,c,d=3} = obj
//a = 1 b = 1 c = undefined d = 1 如果d没有赋值,d默认是3
属性也可以解构赋值
let user = { name: 's', surname: "t"};
[user.name, user.surname] = [1,2]
let o = {a:1, b:2};
[o.a, o.b] = [3,4]
obj // => {3,4}
let options = {
size: {
width: 100,
height: 200
},
items: [1,2],
extra: true
}
let { size: { width,height}, items: [item1]} = options
函数参数解构赋值
直接写入变量,属性名为变量名
function f(x, y) {
return {x, y};
}
// 等同于
function f(x, y) {
return {x: x, y: y};
}
f(1, 2) // Object {x: 1, y: 2}
let metaData = {title:"hello", list:[{title:"111"},{title:"2222"}]}
let {title:eTitle,list:[{title:bTitle}]} = metaData;
// eTitle = hello , dTitle = 111
模块的解构赋值
import runtime from '../../runtime'
const { init, config } = runtime
//可以直接写成
Import {init, config } from ‘../../runtime'