hello,JS:01数据类型、运算符、运算符优先级

前言:

最近在学习javascript中数据类型、运算符,以及运算符优先级等相关基础知识,相对简单、基础但也很重要。加上老师的推荐看了阮一峰老师所写的《javascript教程》。从中刷新了过往认知的概念和固有的规则,明白了javascript的出现会打破以往的惯性思维。在这里1+1不再一定等于2,你常见的算数符号也不是你通常认为的那种作用,一个数字可以是表达式,同时一个表达式也是一个值。。。所以学习这样一种新知识,你要学会先接受,然后敢于去打破自身的思维惯性,之后才能勇于迎接更多挑战。

自我总结:

值===数据类型——转化为:数值(number)/字符串(string)/布尔值(boolean)/undefined/null/对象(object)——转为Nan或者数字(其中:(1)数字+undefined===Nan (2)null===(undefined===Nan ) ——判断布尔值true or false

一、基本语法

1、调试

打开chrome开发工具:右键——检查——console(esc键调取另一个console)

  • Win F12
  • Mac Command + Option + I

2、语句

(1)语句 VS 表达式
A、区别:

  • 语句,主要为了进行某种任务而进行的操作,一般情况下不需要返回值;
  • 表达式,为了得到返回值的计算式,一定会返回一个值。凡是JavaScript语言中预期为值的地方,都可以使用表达式。比如,赋值语句的等号右边,预期是一个值,因此可以放置各种表达式

B、例子:

  • 赋值语句:var a = 1 + 3; //先用var命令,声明了变量a,然后将1 + 3的运算结果赋值给变量a
  • 表达式:1 + 3

(2)分号
分号前面可以没有任何内容,JavaScript引擎将其视为空语句。;;;
A、语句,以分号结尾。
一个分号就表示一个语句结束。多个语句可以写在一行内。不需要加分号,换行默认上一行为语句。

1
2
3
4
5
6
var a =  1  +  3  ;  
var b = 'abc';
/*或*/
var a = 3
var b = 4
var c = a+b

B、表达式不需要分号结尾。一旦在表达式后面添加分号,则JavaScript引擎就将表达式视为语句,这样会产生一些没有任何意义的语句。下面两行语句有返回值,但是没有任何意义,因为只是返回一个单纯的值,没有任何其他操作。

1
2
1  +  3;  //语句,但无任何意义 
'abc';

如图:image

3、变量

(1)定义
最前面的var是变量声明命令。它表示通知解释引擎,要创建一个变量a(相当于申请了1个内存放在变量a这里)。
变量是对“值”的引用,使用变量等同于引用一个值。每一个变量都有一个变量名。如:
var a = 1;

(2)变量提升
A、定义:
变量提升(hoisting),即JavaScript引擎的工作方式是:先解析代码获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有变量的声明语句,都会被提升到代码的头部,如:

1
2
3
4
5
6
7
8
9
10
11
//假设 
var a = 1

/*这里会产生一个变量提升*/
var a ;
a =1;

/* 或*/
a = 3
var a
/* 控制台显示并不会报错,只要有var变量命令的声明,默认情况下就会自动提升到最前面,之后再作赋值*/

总结: 变量提升只对var命令声明的变量有效,如果一个变量不是用var命令声明的,就不会发生变量提升。

B、说说变量提升的几种场景
第1种场景:

1
console.log(a);  var a =  1;

代码首先使用console.log方法,在控制台显示变量a的值。这时变量a还没有声明和赋值,所以这是一种错误的做法,但是实际上不会报错。因为有var存在,即也存在变量提升,引擎中真正运行的是下面这段代码:

1
var a; console.log(a); a =  1;

最后的结果是显示undefined,表示变量a已声明,且位于最前面,但还未赋值。
注: 这种变量提升的技巧很重要,与之后函数作用,复杂函数执行时所出现的一些情况都可解释

第2种场景:
console.log(b); b = 1;
语句将会报错,提示“ReferenceError:b is not defined”,即变量b未声明,这是因为b不是用var命令声明的,JavaScript引擎不会将其提升,而只是视为对顶层对象的b属性的赋值。

4、标识符

(1)定义:
标识符(identifier),用来识别具体对象的一个名称。最常见标识符:变量名、函数名。
注: JavaScript语言的标识符对大小写敏感,所以a和A是两个不同的标识符。

(2)标识符命名规则
A、第一个字符,可以是任意Unicode字母(包括英文字母和其他语言的字母),以及美元符号($)和下划线(_)。
B、第二个字符及后面的字符,除了Unicode字母、美元符号和下划线,还可以用数字0-9。
C、一些命名实例:

  • 下面这些都是合法的标识符。

    1
    2
    3
    4
    arg0
    _tmp
    $elem
    π
  • 下面这些则是不合法的标识符。

    1
    2
    3
    4
    5
    1a  // 第一个字符不能是数字
    23 // 同上
    *** // 标识符不能包含星号
    a+b // 标识符不能包含加号
    -d // 标识符不能包含减号或连词线
  • 中文是合法的标识符,可以用作变量名。

    1
    var 临时变量 = 1;
  • JavaScript有一些保留字,不能用作标识符:

    arguments、break、case、catch、class、const、continue、debugger、default、delete、do、else、enum、eval、export、extends、false、finally、for、function、if、implements、import、in、instanceof、interface、let、new、null、package、private、protected、public、return、static、super、switch、this、throw、true、try、typeof、var、void、while、with、yield

  • 三个词具有特别含义,也不应该用作标识符:

    1
    2
    3
    Infinity
    NaN
    undefined

5、注释:

1
2
3
4
5
6
7
8
9
// 这是单行注释
/*
这是
多行
注释
*/

//历史上JavaScript兼容HTML代码的注释,所以<!--和-->也被视为单行注释。
x = 1; <!-- x = 2;--> x = 3;

6、区块

(1)定义 JavaScript使用大括号,将多个相关的语句组合在一起,称为区块(block)。
(2)与大多数编程语言不一样,JavaScript的区块不构成单独的作用域(scope)。即区块中的变量与区块外的变量,属于同一个作用域。如:

1
2
3
4
{
var a = 1;
}
a // 1

二、数据类型

1、定义

JS语言的每一个值,都属于某一种数据类型。

2、值所属的数据类型分类

JavaScript 的数据类型,共有6种:

注: ES6 又新增了第七种 Symbol 类型的值
(1)数值(number):值为整数和小数(比如1和3.14)
(2)字符串(string):值为字符组成的文本(比如"Hello World"
(3)布尔值(boolean):值判定为true(真)和false(假)两个特定值

注: 用于询问的便可用boolean进行变量命名,如:
var isBoy = true

(4)undefined:值判定为未处理,未定义或不存在。目前未定义所以此处暂时没有任何值,但之后可以去放东西。
注: 一个变量没有赋值,只能是undefined,不会是null

(5)null:值为表示经过处理之后的无值,即此处的值就是“无”的状态。

(6)对象(object):或称“引用类型”,各种值组成的集合。对象=属性+值
对象又可以分成三个子类型:

  • 狭义的对象(object)
  • 数组(array)
  • 函数(function)

3、typeof 运算符(用来确定值所属的数据类型)

(1)三种运算符用来确定数据类型
运算符,用于连接简单表达式,组成一个复杂的表达式(即通过一个关键字,后面加一个变量或值,得出一个结果)

JavaScript有三种方法,可以确定一个值到底属于什么类型。

  • typeof运算符
  • instanceof运算符
  • Object.prototype.toString方法

(2)作用
typeof可以判断一个变量或可以返回一个值为哪种数据类型,如:
※数值、字符串、布尔值分别返回numberstringboolean

1
2
3
4
//数值、字符串、布尔值分别返回number、string、boolean。
typeof 123// "number"
typeof '123'// "string"
typeof false// "boolean"

※函数返回function。

1
2
3
4
5
function f(){

}
typeof f
// "function"

undefined返回 undefined

1
2
typeof  undefined
// "undefined"

※ 利用这一点,typeof用来检查一个没有声明的变量,而不报错。如代码中变量v没有用var命令声明,直接使用就会报错;但放在typeof后面就不报错了,而是返回undefined

1
2
3
4
5
v
// ReferenceError: v is not defined

typeof v
// "undefined"

※实际编程中,变量v要用var命令声明,这个特点通常用在判断语句。

1
2
3
4
5
6
7
8
9
10
// 错误的写法
if (v) {
// ...
}
// ReferenceError: v is not defined

// 正确的写法
if (typeof v === "undefined") {
// ...
}

※除此以外,其他情况都返回object

1
2
3
4
5
//除此以外,其他情况都返回object
typeof window // "object"
typeof {} // "object"
typeof [] // "object"
typeof null // "object"

实际操作中:
image

如何判断一个变量是否为函数?
image

4、布尔值

(1)作用
布尔值代表“真”和“假”两个状态。“真”用关键字true表示,“假”用关键字false表示。布尔值只有这两个值。
(2)下列运算符会返回布尔值
A、两元逻辑运算符:&&(And),||(Or)
B、前置逻辑运算符:!(Not)
C、相等运算符:===!====!=
D、比较运算符:>>=<<=
如:4>3-->true

(3)如果JavaScript预期某个位置应该是布尔值,会将该位置上现有的值自动转为布尔值。转换规则是除了下面六种值被转为false,其他值都视为true。

  • undefined –>false
  • null –>false
  • false –>false
  • +0-0NaN –> false【其他number(数字)为true】
  • ""''(空字符串)–>false【其他string(字符串,包含" "空白字符串)为true】

A、关于""''(空字符串)布尔值往往用于程序流程的控制,如:

1
2
3
4
5
if ('') {
console.log(true);
}
/* 没有任何输出*/
/*上面代码的if命令后面的判断条件,预期应该是一个布尔值,所以JavaScript自动将空字符串,转为布尔值false,导致程序不会进入代码块,所以没有任何输出*/

B、空数组([])和空对象({})对应的布尔值,都是true。如:

1
2
3
4
5
6
7
8
9
if ([]) {
console.log(true);
}
// true

if ({}) {
console.log(true);
}
// true

三、数据类型转换解密

总结:js的数据类型如何判断,即任何表达式先转字符串再转数字

1、if判断

(1)js如何转换判断
从面试题说说if的数据类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 题目1:如下代码输出什么?
if ("hello") {
console.log("hello")
} //true

// 题目2:如下代码输出什么?
if ("") {
console.log('empty')
}//空字符串,代表里面什么都没有 //false

// 题目3:如下代码输出什么?
if (" ") {
console.log('blank')
}//空白字符串,仍然代表一个值,只不过是空值 //true

// 题目4:如下代码输出什么?
if ([0]) {
console.log('array')//对象 true
}

if([]){
console.log('hahah')
} //特殊对象 true

// 题目5:如下代码输出什么?
if('0.00'){
console.log('0.00')
} //字符串(除空字符串外)true

if(+0.00){
console.log('hahah')
} //false

(2)判断原理:
对于if()里括号的表达式(如以下),会被强制转换为布尔类型

  • undefined –> false
  • null –>false
  • false` –>false
  • +0-0NaN –>false 【其他number(数字)为true】
  • ""''(空字符串)–>false【其他string(字符串,包含” “空白字符串)为true】

2、==判断

(1)js处理 ==的表达式 判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
"" == 0  //题目1   ""空字符串===false===0  0==0  //true
" " == 0 //题目2 Number(' ')-->0===false 0==0 //true
"" == true //题目3 0===1 //false
"" == false //题目4 0===0 //true
" " == true //题目5 false===0 0==1 //false

!" " == true //题目6 " "空白字符串===true !0===false===0 0不等于true //false
!" " == false //题目7 false==false //true
*"hello" == true //题目8 字母字符串===Nan Nan不等于1 //fasle
*"hello" == false //题目9 字母字符串===Nan Nan不等于0 //fasle
"0" == true //题目10 "0"(数字字符串)===0不等于1 //false
"0" == false //题目11 "0"(数字字符串)===0===0 //true
"00" == false //题目12 "00"(数字字符串)===00===0 //true
"0.00" == false //题目13 "0.00"(数字字符串)===0===0 //true
undefined == null //题目14 undefined===Nan===null //true
{} == true //题目15 {}===object(没有字符串)不等于true //fasle

[] == true //题目16 [](空数组)===object(没有字符串)不等于true //fasle

var obj = {
a: 0,
valueOf: function(){return 1}
}
obj == "[object Object]" //题目17 //false
obj == 1 //题目18 //true
obj == true //题目19 //true

(2)判断原理
两图对照,就能判断表达式的数据类型:
image
image

四、运算符

1、作用:

运算符,主要用于连接简单表达式,组成一个复杂的表达式
typeof,为运算符。即typeof 100 === 'numeber' 是一个值为字符串'numeber'的表达式
再如:

  • 3 : 表达式
  • 3+4:表达式
    + :为运算符
  • (1+2)*5:表达式。
    +* : 均为运算符

2、判断参考标准:

(1)有些操作符对不同的数据类型有不同的含义,比如+,在两个操作数都是数字的时候,会做加法运算
image

(2)两个参数都是字符串或在有一个参数是字符串的情况下会把另外一个参数转换为字符串,做字符串拼接
image

(3)在参数有对象的情况下会调用其valueOf或toString的函数(两者同时使用,注意优先级)
image
image

(4)在只有一个字符串参数的时候会尝试将其转换为数字
image

注: 如果字符串无法转换成数字,那么则转换失败,通常结果为:Nan
image

(5)在只有一个数字参数的时候返回其正数值

1
2
3
4
5
6
7
8
console.log(2+4);   //6     加法运算
console.log("2"+"4"); //"24"为字符串 字符串的拼接
console.log(2+"4"); //"24" 一个数字+字符串,会把数字转化成字符串,然后再进行拼接,
不是一个连读的数字24,而是2和4的分开读
console.log(2+new Date());//"2Mon Jan 20 2014 17:15:01 GMT+0800 (China Standard Time)"

/* 一个数字+一个对象,会调用这个对象的valueOf或toString这个方法*/
console.log(+"4");//4

3、常见类型:

  • 算数表达式
  • 比较表达式
  • 逻辑表达式
  • 赋值表达式
  • 单目运算符
  • 关键字作为运算符,如typeofdeleteinstanceof

(1)算数表达式
A、加法运算符(Addition):x + y
B、减法运算符(Subtraction):x - y
C、乘法运算符(Multiplication):x * y
D、除法运算符(Division):x / y
加减乘除的运算,这里会尽可能将字符串转化成数字,如果转换不了数字,则会得出Nan这个结果
image

E、余数运算符(Remainder):x % y 用于循环语句
image

F、自增运算符(Increment):x ++或者++x

  • x++,由简单表达式(x)和运算符(++)组成一个复杂的表达式,一个表达式本身整体,就是一个值,那么x++的值就是x的原始值;
  • x++,作为表达式的结果是是它自己本身,同一作用域中的下一个变量,则是x++内部又自增了1,即x=x+1

注:

  • x++:是自增前x的原始值
  • ++x:是自增后下一个x变量的值
    如:image

++ e则是自增后的那个值,图1所示,自增+1的e ===10,那么++e === 11

  • 自减运算符(Decrement):--x或者 x--
    同上可得
  • 求负运算符(Negate):-x
  • 数值运算符(Convert to number):+x

(2)赋值运算符

1
2
3
4
5
6
7
8
9
10
11
x += y // 等同于 x = x + y
x -= y // 等同于 x = x - y
x *= y // 等同于 x = x * y
x /= y // 等同于 x = x / y
x %= y // 等同于 x = x % y
x >>= y // 等同于 x = x >> y
x <<= y // 等同于 x = x << y
x >>>= y // 等同于 x = x >>> y
x &= y // 等同于 x = x & y
x |= y // 等同于 x = x | y
x ^= y // 等同于 x = x ^ y

(3)比较运算符
比较运算符比较两个值,然后返回一个布尔值(实际上是ture或false),表示是否满足比较条件。JavaScript提供了8个比较运算符。
A、= 赋值
= 为赋值运算符,连接两个简单的表达式构成复杂的表达式,如x=y即为表达式,若将其看成一个整体,即会输出一个值,这个值则为最终赋的值,如:
image

B、== 相等
这里的相等是,近似相等,后台得到的值则是ture或者false如数字和字符串的比较,后台会将字符串做一个类型转换:
image

C、===严格相等
这里的相等则更严格,值和类型的严格相等

题外话: === VS ==

  • ===叫做严格运算符
  • == 叫做相等运算符

关于这两者的区别我在知乎上看到一篇文章:Javascript 中 == 和 === 区别是什么?

a、严格运算符的运算规则如下:
(1)不同类型值
如果两个值的类型不同,直接返回false
(2)同一类的原始类型值
同一类型的原始类型的值(数值、字符串、布尔值)比较时,值相同就返回true,值不同就返回false
(3)同一类的复合类型值
两个复合类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个对象。
(4)undefinednull
undefinednull与自身严格相等。

1
2
null  === null  //true
undefined === undefined // true

b、相等运算符在比较相同类型的数据时,与严格相等运算符完全一样。在比较不同类型的数据时,相等运算符会先将数据进行类型转换,然后再用严格相等运算符比较。类型转换规则如~下:
(1)原始类型的值
原始类型的数据会转换成数值类型再进行比较。字符串和布尔值都会转换成数值,所以题主的问题中会有第二个string输出。
(2)对象与原始类型值比较
对象(这里指广义的对象,包括数值和函数)与原始类型的值比较时,对象转化成原始类型的值,再进行比较。
(3)undefinednull
undefinednull与其他类型的值比较时,结果都为false, 它们互相比较时结果为true。
(4)相等运算符的缺点
相等运算符隐藏的类型转换,会带来- -些违反直觉的结果。

1
2
3
4
5
6
7
8
9
== '0'                 // false
0 == // true
0 == // true
false == 'false' // false
false == '0' //true
false == undefined // false
false == null // false
null == undefined // true
' \t\r\n ' == 0 // true

这就是为什么建议尽量不要使用相等运算符。至于使用相等运算符会不会对后续代码造成意外影响,答案是有可能会。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 var a = undefined;
if(!a){
console.log("1");
}
//1

var a = undefined;
if(a == null){
console.log("1");
}
//1

var a = undefined;
if(a === null){
console.log("1");
}
// 无输出

也就是说当a为undefined时,输出的值会有变化,而在编程中对象变成undefined实在是太常见了。
D、 != 不相等
E 、!== 严格不相等
F、 < 小于
G、 <= 小于或等于
H、 > 大于
I 、 >= 大于或等于

(5)布尔运算符
A、! 取反运算符
B、&& 且运算符
表达式&&表达式 会自动地转化成ture或者false 进行比较。或判断一个东西是否存在,是否满足条件
C、|| 或运算符
初始化赋值的时候可用

题外话:
&& 运算符 VS || 运算符
a、——&& :用于判断一个东西是否存在,或是否满足条件(有false就判断)
如果第一个为true,第二个也为true,最终值就是第二个值;
如:var a = “hello”,那么a&&console.log(a)就能进行判断,第二个值的最终结果为true

  • 如果第一个为true,再看第二个是否为false,那最终的值就是为第一个值;
  • 如果第一个为false,那就不用再判断第二个,就是第一个值
    总结: 如果是false就不用再看了;如果有true,再看第二个

b、——||: 用于初始化值
如果第一个为true,后面就不用再管;如果第一个为false,再看第二个
总结: 如果是true,就不会再管了;如果有false,再看第二个。只要一项为真,那就不用管,如:
image
第二个cc为undefined,转化为Boolean类型为false,||中还会看第二个,0为true,那么结果就为0。将0赋值给第一个cc,那么cc则有了初始值。假如cc=100,再执行cc=cc||0,那么cc仍然等于100,转换为boolean类型为true,那么则不需要在看第二个数
如果cc之前没有声明的话,得到的是一个默认值;如果cc之前声明过,值还保持不变。

D、condition? true case : false case三元条件运算符
题外话: 三目运算符 Condition?true case:false case
这里有一个条件判断,如果条件判断为真的话,那么整个表达式的结果就是true case执行的结果;如果条件判断为假的话,那么整个表达式的结果就是false case执行的结果,当然,false case还是会再做一些执行,如:

1
2
3
4
5
6
if (a > 10) {
b = a
}
else {
b = a - 2
}

答:b = a>10? a : a-2

(5)位运算符
必要时看看二进制转换对照表也行:
二进制转换对照表

A、或运算(or):符号为|,表示两个二进制位中有一个为1,则结果为1,否则为0。
image
B、与运算(and):符号为&,表示两个二进制位都为1,则结果为1,否则为0。
image
C、否运算(not):符号为,表示将一个二进制位变成相反值。
D、异或运算(xor):符号为ˆ,表示两个二进制位中有且仅有一个为1时,结果为1,否则为0。
E、左移运算(left shift):符号为<<
F、右移运算(right shift):符号为>>
J、带符号位的右移运算(zero filled right shift):符号为>>>

(6)其它运算符(考虑优先级)
A、小括号
在JavaScript中,圆括号是一种运算符,它有两种用法:
如果把表达式放在圆括号之中,作用是求值;如果跟在函数的后面,作用是调用函数。
B、void
void运算符的作用是执行一个表达式,然后返回undefined
image
C、逗号运算符
逗号运算符用于对两个表达式求值,并返回后一个表达式的值。如:
image

五、运算符优先级和结合性

1、定义

结合性是指多个具有同样优先级的运算符表达式中的运算顺序

2、实践——理论:

(1)有的运算符是左结合的,即运算从左到右执行,下面两个运算是一样的

1
2
w = x + y + z;
w = (x + y) + z;

(2)有的运算符是右结合的

1
2
3
4
w = x = y = z;
w = (x = (y = z));
w = a: b: c ? d : e? f : g;
w = a? b : (c? d: (e? f : g));

如:
image

  • 最高:typeof
  • 最低:

3、理论——实践

几个优先级从高到低: typrof () ++ -- ! +-*% && || =

(1)typeof的优先级相当的高,比加减乘除都优先,所以虽然是操作符,但在复杂表达式的时候我们还是习惯加括号,如:

1
2
3
typeof 2*3;//NaN
typeof (2*3);//"number"
typeof 2+3;// "number3"

题外话: NanN(即not and number)
定义:是一个数字类型,不过它不是一个有效的数,表示为错误数字。
通过number函数可以把一个数字的字符创转化成数字,不过无法将一个字母的字符串转化成数字,得出结果为NaN。当NaN===NaN—>false,一个数字与自己不相等,作为一个数字,二者对等与否对方均无从得知,没有突出一个数字的识别性。如:
image

(2)++--是右结合的操作符(优先级最高的几个都是右结合),而且比加减乘除优先级高。同时自增、自减运算符的运算数得是左值(可以放在赋值符号左边的值),而不能是常数

1
2
3
4
5
4++; //ReferenceError: Invalid left-hand side expression in postfix operation
var a=0,b=0;//, 忽略第一个操作数,返回第二个操作数 L(往左运算)
a+++b;//0
a;//1,++优先级比+高,所以相当于(a++)+b
b;//0

(3)赋值运算符的优先级相当的低
a = b == c; //等同于a = (b==c)

(4)逻辑非!也在优先级队列的前端,比加减乘除高,但逻辑与、逻辑或优先级很低,不如加减乘除
!2*0; //0, 等价于(!2)*0

(5)一个关于逻辑运算符的有意思地方是其“短路”功能,会结合表达式计算值来判断

1
2
3
1 && 3;
1 && "foo" || 0;
1 || "foo" && 0

如:image

-------------本文结束感谢您的阅读-------------