前端入门9-JavaScript语法之运算符
声明
本系列文章内容全部梳理自以下几个来源:
- 《JavaScript权威指南》
- MDN web docs
- Github:smyhvae/web
- Github:goddyZhao/Translation/JavaScript
作为一个前端小白,入门跟着这几个来源学习,感谢作者的分享,在其基础上,通过自己的理解,梳理出的知识点,或许有遗漏,或许有些理解是错误的,如有发现,欢迎指点下。
PS:梳理的内容以《JavaScript权威指南》这本书中的内容为主,因此接下去跟 JavaScript 语法相关的系列文章基本只介绍 ES5 标准规范的内容、ES6 等这系列梳理完再单独来讲讲。
正文-运算符
程序中的代码其实就是利用各种运算符来辅助完成各种指令功能,在 JavaScript 中,有一些不同于 Java 中的运算符处理,这次就来讲讲这些运算符。
由于我已经有了 Java 的基础了,本节不会讲基础的运算符介绍,比如算术表达式中的加减乘除取余等、关系表达式中的大于小于等、逻辑表示式中的自增、自减、移位等等,这些基础运算符的含义、用法、优先级这些跟 Java 基本没有区别,所以就不介绍了。
下面着重讲一些在 JavaScript 比较不同的行为的一些运算符:
"+" 运算符
任何数据类型的变量都可以通过 "+" 运算符来进行计算,所以它有一套处理规则,通常要么就是按数字的加法运算处理、要么就是按照字符串的拼接处理,处理规则如下:
- 如果操作数中存在对象类型,先将其按照上节介绍的转换规则,转成原始值;
- 如果操作数已经全部是原始值,此时如果有字符串类型的原始值,那么将两个原始值都转为字符串后,按字符串拼接操作处理;
- 如果操作数已经全部是原始值且没有字符串类型的,那么将操作数都转为数字类型后,按数字的加法处理;
- NaN 加上任意类型的值后都是 NaN.
以上的处理规则是针对于通过 "+" 运算符处理两个操作数的场景,如果一个表达式中存在多个 "+" 运算符,那么分别以优先级计算过程中,每一次计算 "+" 运算符的两个操作数使用上述规则进行处理。
举个例子:
1 + 2 // => 3, 因为操作数都是数字类型的原始值
1 + "2" // => "12",因为操作数中存在字符串类型的原始值,所以是按字符串拼接来处理
1 + {} // => "1[object Object]",因为有操作是对象类型,先将其转为原始值,{} 转为原始值为字符串 "[object Object]",所以将操作数都转为字符串后,按字符串拼接处理
1 + true // => 2,因为两个都是原始值,且没有字符串类型,所以将 true 转为数字类型后是 1,按加法处理
1 + undefined // => NaN,因为 undefined 转为数字类型后为 NaN,NaN 与任何数运算结果都为 NaN
1 + 2 + " dasu" // => "3 dasu", 因为先计算 1+2=3,然后再计算 3 + " dasu",所以是 "3 dasu"
1 + (2 + " dasu") // => "12 dasu",因为先计算 2 + " dasu" = "2 dasu",再计算 1 + "2 dasu" = "12 dasu"
因为 "+" 运算符在编程中很常见,也很常用,而 JavaScript 又是弱类型语言,变量无需声明类型,那么程序中,"+" 运算符的两个操作数究竟是哪两种类型在进行计算,结果又会是什么,这点在心里至少是要明确的。
"" 和 "=" 相等运算符
"" 和 "=" 都是用于判断两个操作数是否相等的运算符,但它们是有区别的。
"==" 比较相等的两个操作数会自动进行一些隐式的类型转换后,再进行比较,俗称不严格相等。
"===" 比较相等的两个操作数,不会进行任何类型转换,相等的条件就是类型一样,数值也一样,所以俗称严格相等。
而 "!=" 和 "!==" 自然就是这两个相等运算符的求反运算。下面分别来看看:
"==="
当通过这个运算符来比较两个操作数是否严格相等时,具体规则如下:
- 如果两个操作数的类型不相同,则它们不相等
- 如果其中一个操作数是 NaN 时,则它们不相等(因为 NaN 跟任何数包括它本身都不相等)
- 如果两个操作数都是对象类型,那么只有当两个操作数都指向同一个对象,即它们的引用一样时,它们才相等
- 如果两个操作数都是字符串类型时,当字符串一致时,在某些特殊场景下,比如具有不同编码的 16 位值时,它们也不相等,但大部分情况下,字符串一致是会相等,但要至少清楚不是百分百
- 如果两个操作数都是布尔类型、数字类型、null、undefined,且值都一致时,那它们相等
总之,这里的规则跟 Java 里的相等比较类似,Java 里没有严格不严格之分,它处理的规则就是按照 JavaScript 这里的严格相等来处理,所以大部分比较逻辑可参考 Java。
需要注意的就是,NaN 与任何数包括它本身也不相等、同一个字符串内容可能会有不同的编码值,所以并不是百分百相等。
"=="
这个通常称为不严格相等,当比较是否相等的两个操作数的数据类型不一样时,会尝试先进行转换,然后再进行比较,相比于上面的 "===" 严格相等运算符来说,它其实就是放宽了比较的条件,具体规则如下:
- 如果两个操作数的类型一样,那么规则跟 "===" 一样
- 如果一个类型是 null,另一个类型是 undefined,此时,它们也是相等的
- 如果一个类型是数字,另一个类型是字符串,那么先将字符串转为数字,再进行比较
- 如果一个类型是布尔,先将布尔转成 1(true)或 0(false),然后再根据当前两个类型是否需要再进一步处理再比较
- 如果一个类型是对象,那么先将对象转换成原始值,然后再根据当前两个类型是否需要再进一步处理再比较
总之,"" 的比较相对于 "=" 会将条件放宽,下面可以看些例子:
null === undefined // => false,两个类型不一样
null == undefined // => true,不严格情况下两者可认为相等
1 == "1" // => true,"1" 转为数字 1 后,再比较
1 == [1] // => true,[1] 先转为字符串 "1",此时等效于比较 1 == "1",所以相等
2 == true // => false,因为 true 先转为数字 1,此时等效于比较 2 == 1
"&&” 逻辑与
逻辑与就是两个条件都要满足,这点跟 Java 里的逻辑与操作 && 没有任何区别。
但 JavaScript 里的逻辑与 && 操作会更强大,在 Java 里,逻辑与 && 运算符的两个操作数都必须是关系表达式才行,而且整个逻辑与表达式最终的结果只返回 true 或 false。
但在 JavaScript 里,允许逻辑与 && 运算符的两个操作数是任意的表达式,而且整个逻辑与 && 表达式最终返回的值并不是 true 或 false,而是其中某个操作数的值。
什么意思,来看个例子:
x == 0 && y == 0
这是最基本的用法,跟 Java 没有任何区别,当且仅当 x 和 y 都为 0 时,返回 true,否则返回 false。
上面那句话,是从这个例子以及延用 Java 那边对逻辑与 && 运算符的理解所进行的解释。
但实际上,在 JavaScript 里,它是这么处理逻辑与 && 运算符的:
- 如果左操作数的值是假值,那么不会触发右操作数的计算,且整个逻辑与 && 表达式返回左操作数的值
- 如果左操作数的值是真值,那么整个逻辑与 && 表达式返回右操作数的值
- 假值真值可以通俗的理解成,上节介绍各种数据类型间的转换规则中,各类型转换为布尔类型的值,转为布尔后为 true,表示这个值为真值。反之,为假值。
所以,按照这种理论,我们再来看看上面那个例子,首先左操作数是个关系表达式:x == 0
,如果 x 为 0,这个表达式等于 true,所以它为真值,那么整个逻辑与 && 表达式返回右操作数的值。右操作数也是个关系表达式:y == 0
,如果 y 也等于 0,右操作数的值就为 true,所以整个逻辑与 && 表达式就返回 true。
虽然结果一样,但在 JavaScript 里对于逻辑与 && 表达式的解释应该按照第二种,而不是按照第一种的 Java 里的解释。如果还不理解,那么再来看几个例子:
function getName() {
return "dasu"
}
null && getName() //输出 => null,因为左操作数 null 转成布尔是 false,所以它是假值,所以逻辑与 && 直接返回左操作数的值 null
getName && getName() //输出 => "dasu",因为左操作数是一个函数对象,如果该函数对象被声明定义了,那么转为布尔值就是 true,所以逻辑与 && 表达式返回右操作数的值,右操作数是 getName(),调用了函数,返回了 "dasu",所以这个就是这个逻辑与 && 表达式的值。
第一个逻辑与表达式:null && getName()
会输出 null,是因为左操作数 null 转成布尔是 false,所以它是假值,所以逻辑与 && 直接返回左操作数的值 null。
第二个逻辑与表达式:getName && getName()
会输出 "dasu",是因为左操作数是一个函数对象,如果该函数对象被声明定义了,那么转为布尔值就是 true,所以逻辑与 && 表达式返回右操作数的值,右操作数是 getName(),调用了函数,返回了 "dasu",所以这个就是这个逻辑与 && 表达式的值。
所以 JavaScript 里的逻辑与 && 表达式会比 Java 更强大,它有一种应用场景:
应用场景
function queryName(callback) {
//...
//回调处理
callback && callback();
}
在 Java 中,我们提供回调机制的处理通常是定义了一个接口,然后接口作为函数的参数,如果调用的时候,传入了这个接口的具体实现,那么在内部会去判断如果传入的接口参数不为空,就调用接口里的方法实现通知回调的效果。
在 JavaScript 里实现这种回调机制就特别简单,通过逻辑与 && 表达式,一行代码就搞定了,如果有传入 callback 函数,那么 callback 就会是真值,逻辑与 && 表达式就会去执行右操作数的 callback()。
当然,如果你想严谨点,你可以多加几个逻辑与 && 表达式来验证传入的 callback 参数是否是函数类型。
"||" 逻辑或
逻辑或 || 跟逻辑与 && 就基本是一个东西了,理解了上面讲的逻辑与 && 运算符的理论,那么自然也就能够理解逻辑或 || 运算符了。
它们的区别,仅在于对表达式的处理,逻辑或 || 表达式是这么处理的:
- 如果左操作数的值是真值,那么不会触发右操作数的计算,且整个逻辑或 || 表达式返回左操作数的值
- 如果左操作数的值是假值,那么整个逻辑或 || 表达式返回右操作数的值
- 假值真值可以通俗的理解成,上节介绍各种数据类型间的转换规则中,各类型转换为布尔类型的值,转为布尔后为 true,表示这个值为真值。反之,为假值。
这里就直接来说下它的一个应用场景了:
应用场景
function queryNameById(id) {
//参数的默认值
id = id || 10086;
//...
}
处理参数的默认值,如果调用函数时,没有传入指定的参数时。
当然,还有其他很多应用场景。总之,善用逻辑与 && 和逻辑或 || 运算符,可以节省很多编程量,同时实现很多功能。
"," 逗号运算符
在 Java 中,"," 逗号只用于在声明同一类型变量时,可同时声明,如:
int a, b, c;
在 JavaScript 里,"," 逗号运算符同样具有这个功能,但它更强大,因为带有 "," 逗号运算符的表达式会有一个返回值,返回值是逗号最后一项操作数的值。
逗号运算符跟逻辑与和逻辑或唯一的区别,就在于:逗号运算符会将每一项的操作数都进行计算,而且表示式一直返回最后一项的操作数的值,它不管每个操作数究竟是真值还是假值,也不管后续操作数是否可以不用计算了。
举个例子:
function getName() {
return "dasu"
}
function queryNameById(id, callback) {
id = id || 10086;
callback && callback();
}
function myCallback() {
console.log("I am dasu");
}
var me = (queryNameById(0, myCallback), getName()) //me会被赋值为 "dasu",且控制台输出 "I am dasu"
变量 me 会被赋值为 "dasu",且控制台输出 "I am dasu"。
typeof 运算符
返回指定操作数的数据类型,例:
在 JavaScript 中数据类型大体上分两类:原始类型和引用类型。
原始类型对应的值是原始值,引用类型对应的值为对象。
对于原始值而言,使用 typeof 运算符可以获取原始值所属的原始类型,对于函数对象,也可以使用 typeof 运算符来获取它的数据类型,但对于其他自定义对象、数组对象、以及 null,它返回的都是 object,所以它的局限性也很大。
delete 运算符
delete 是用来删除对象上的属性的,因为 JavaScript 里的对象有个特性,允许在运行期间,动态的为对象添加某个属性,那么,自然也允许动态的删除属性,就是通过这个运算符来操作。
这个在对象一节还会拿出来讲,因为并不是所有的属性都可以成功被删除的,属性可以设置为不可配置,此时就无法通过 delete 来删除。
另外,之前也说过,在函数外声明的全局变量,本质上都是以属性的形式被存在在全局对象上的,但这些通过 var 或 function 声明的全局变量,无法通过 delete 来进行删除。
之前也说过,如果在声明变量时,不小心漏掉了 var 关键字,此时程序并不会出异常,因为漏掉 var 关键字对一个不存在的变量进行赋值操作,会被 js 解释器认为这行代码是要动态的为全局对象添加一个属性,这个动态添加的属性就可以通过 delete 来进行删除,因为动态添加的属性默认都是可配置的。
instanceof 运算符
在 Java 中,可以通过 instanceof 运算符来判断某个对象是否是从指定类实例化出来的,也可以用于判断一群对象是否属于同一个类的实例。
在 JavaScript 中有些区别,但也有些类似。
var b = {}
function A() {}
A.prototype = b;
var a = new A();
if (a instanceof A) { //符合,因为 a 是从A实例化的,继承自A.prototype即b
console.log("true");
}
function B() {}
B.prototype = b;
var c = new B();
if (c instanceof A) {//符合,虽然c是从B实例化的,但c也同样继承自b,而A.prototype指向b,所以满足
console.log("true");
}
if (c instanceof Object) {//符合,虽然 c 是继承自 b,但 b 继承自 Object.prototype,所以c的原型链中有 Object.prototype
console.log("true");
}
在 JavaScript 中,instanceof 运算符的左侧是对象,右侧是构造函数。但他们的判断是,只要左侧对象的原型链中包括右侧构造函数的 prototype 指向的原型,那么条件就满足,即使左侧对象不是从右侧构造函数实例化的对象。
例子代码看不懂么事,这个在后续介绍原型时,还会再拿出来说,先清楚有这么个运算符,运算符大概的作用是什么就可以了。
大家好,我是 dasu,欢迎关注我的公众号(dasuAndroidTv),公众号中有我的联系方式,欢迎有事没事来唠嗑一下,如果你觉得本篇内容有帮助到你,可以转载但记得要关注,要标明原文哦,谢谢支持~
前端入门9-JavaScript语法之运算符的更多相关文章
- javascript语法 1.运算符 2. 流程控制 3. 函数 4. 四种变量 5. 数据类型的运用 6. js页面交互
1.运算符 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <tit ...
- JavaScript学习笔记(8)——JavaScript语法之运算符
一. 算术运算符: 运算符 描述 例子 结果 + 加 x=y+2 x=7 - 减 x=y-2 x=3 * 乘 x=y*2 x=10 / 除 x=y/2 x=2.5 % 求余数 (保留整数) x=y%2 ...
- javascript语法详解
javascript语法:运算符 条件语句if...else... 条件语句switch 循环语句for 循环语句while 跳转语句 js运算符 1.算数运算符:+ - * % / ++ ...
- 前端入门10-JavaScript语法之对象
声明 本系列文章内容全部梳理自以下几个来源: <JavaScript权威指南> MDN web docs Github:smyhvae/web Github:goddyZhao/Trans ...
- 前端入门8-JavaScript语法之数据类型和变量
声明 本系列文章内容全部梳理自以下几个来源: <JavaScript权威指南> MDN web docs Github:smyhvae/web Github:goddyZhao/Trans ...
- 前端入门7-JavaScript语法之相关术语
声明 本系列文章内容全部梳理自以下几个来源: <JavaScript权威指南> MDN web docs Github:smyhvae/web Github:goddyZhao/Trans ...
- 前端入门12-JavaScript语法之函数
声明 本系列文章内容全部梳理自以下几个来源: <JavaScript权威指南> MDN web docs Github:smyhvae/web Github:goddyZhao/Trans ...
- 前端入门11-JavaScript语法之数组
声明 本系列文章内容全部梳理自以下几个来源: <JavaScript权威指南> MDN web docs Github:smyhvae/web Github:goddyZhao/Trans ...
- JavaScript 语法:松软科技前端教程
JavaScript 语法是一套规则,它定义了 JavaScript 的语言结构. var x, y; // 如何声明变量 x = 7; y = 8; // 如何赋值 z = x + y; // 如何 ...
随机推荐
- JavaPoet开源项目的使用
一.介绍 JavaPoet项目可以动态的生成Java文件,这是一个很强大和很动态的方法,使用注解的时候假如需要生成新的Java文件就可以通过这个开源项目实现. 项目地址:https://github. ...
- Python文件操作与函数目录
文件操作 python文件操作 函数 Python函数学习——初步认识 Python函数学习——作用域与嵌套函数 Python函数学习——匿名函数 python内置函数 Python函数学习——递归 ...
- [原创]K8 DB_Owner权限GetShell工具
2011-04-23 02:19:56| 分类: 原创工具 DB_Owner权限拿Shell工具[K.8]Author: QQ吻Team:Crack8_TeamBlog:http://qqhack8 ...
- 测试工具之Fiddler
Fiddler是一款很好的抓包分析工具,里面有很多小功能,这里介绍常用功能 Fiddler下载地址: https://www.telerik.com/download/fiddler 下载完成后,直接 ...
- 人脸识别&ORC的Demo
一.用到的jar包: face_sdk-1.3.4.jar json-20160810.jar ocr_sdk-1.3.4.jar 下载地址:https://files.cnblogs.com/fil ...
- VueJs(1)---快速上手VueJs
[VueJs入门] 版权声明 首先申明:此篇博客不是本人原创,只是最近开始学习vue.jS,看到有作者写的很不错,我仅在它的基础上仅仅是修改了样式 原文博客地址:https://blog.csdn.n ...
- 我可能不懂Array.prototype.sort
今天 fix 我们后台系统的一些 bug.系统是基于 beego 和模板开发的,各种前后端代码揉作一团,没有格式,没有 eslint,全局变量满天飞,连 js 代码都有后端的插值,读起来非常 酸爽. ...
- Android--UI之Gallery
前言 这篇博客讲解一下Android开发中,Gallery控件的使用,这是一个画廊视图,用于展示一组图片,用固定在中间位置的水平滚动列表显示列表项的视图.Android最新的API文档中了解到,在An ...
- karma测试实践
karma是Google团队开发的一套前端测试运行框架,它不同于测试框架(jasmine,mocha等),它运行在这些测试框架之上,主要完成的工作有: 1.karma启动一个web服务器,生成包含js ...
- [java核心篇02]__内部类
前言 其实我们在前面已经初步接触到内部类了,但是我们去对它的作用并不胜了解.只是简单的知道了类的定义也是可以嵌套的,定义在一个类里面的类就是内部类. class out{ private String ...