因为工做缘由,开发语言逐渐以JavaScript为主,因此,抽空学习了下JavaScript语法。等现阶段的工做稳定以后,陆续会分享下本身在学习和开发过程当中的一些经验总结。本着"技多不压身"的原则以及对各类编程语言的热爱,虽然笔者一直从事游戏开发工做,也愉快而又义无反顾的加入了JavaScript阵营。javascript
首先,JavaScript是一门动态类型的编程语言。支持面向对象、函数式等编程范式。同时,它也是运行在宿主环境下的轻量级的脚本语言,例如:浏览器,JavaScript代码可嵌入到HTML页面中。固然,也有应用基于Node.js的服务器环境。能够说它是主流浏览器都支持的脚本语言,这也造就了JavaScript在PC、手机、平板等环境处理与网页交互时的自然优点。随着HTML5的推广与普遍应用,出现了大量基于JavaScript的跨平台框架和游戏引擎,是通往全栈开发很值得学习的一门编程语言。正如在编程语言界流行着“世界终将是JS的”的槽点,足以可见JavaScript的强大。前端
web前端:最近比较火的Vue.js、React、 Angular等等前端框架层出不穷。
手机app:React Native、PhoneGap、Weex等跨平台移动框架等,以及微信小程序等。
游戏引擎:Cocos2d-js、Unity、egret等引擎都支持JavaScript脚本语言。
服务器:pomelo、Node.js等。java
JavaScript语法中大量借鉴了C和Java、Perl等语言的语法,所以熟悉这些语言的人再来学习JavaScript会感受似曾相识。但JavaScript与Java是两回事,JavaScript彻底是蹭了当年Java的热度,来推进本身。web
一个完整的JavaScript实现由下面三个不一样的部分组成:express
ECMAScript:
定义了语言的基础,规定并对该语言在语法、类型、语句、关键字、保留字、操做符、对象等方面做了具体描述,并不包含输入和输出。编程
DOM:
文档对象模型(Document Object Model)是XML和HTML的编程接口。DOM会把整个页面映射为一个多层节点结构树,程序能够对结构树进行添加、删除、替换或修改任何节点。小程序
BOM:
浏览器对象模型(Browser Object Model)支持访问和操做浏览器窗口。微信小程序
注:如下描述就不区分ECMAScript和JavaScript,也不简称js,均统称JavaScript。api
这里的标识符,统指变量名、函数名、属性名以及函数参数的名字。那么,标识符的命名有如下规则:(这些规则跟C、Java等语言相似)数组
注:在JavaScript中,变量名、函数名甚至操做符等是区分大小写的。即name和Name表明的是两个不一样的变量。
单行注释使用//。例如:
// 这是一个单行注释
多行注释使用/***/结构。例如:
/* * 这是一个多行注释 */
启用严格模式,会对一些不肯定的行为或不安全的操做抛出错误。在脚本的顶部添加以下字符串:
"use strict";
也能够添加到函数内部,来指定该函数在严格模式下执行。
JavaScript中每条语句都是以分号结尾,但分号不是必需的。但建议是不要省略分号,来避免如不完整的输入等问题。语句块用花括号包含。
可使用var
关键字来定义变量,JavaScript的变量能够用来保存任何类型的数据。例如:
var name = "AlphaGL"; name = 18; name = function() { };
也可使用逗号来定义多个变量。例如:
var name = "AlphaGL", age = 18, work = true;
虽然JavaScript支持这种写法,通常不推荐。不只可读性差,并且易出错。
在函数内声明的变量,那么该变量的做用域为该函数内。例如:
function test() { var name = "AlphaGL"; } test(); console.log(name); // ReferenceError: name is not defined
虽然能够去掉var声明来解决这个问题,例如:
function test() { name = "AlphaGL"; } test(); console.log(name);
这时,name就成了全局变量。通常不推荐这种作法。全局变量难以维护,并且容易致使结构混乱。
JavaScript中数据类型主要有:Number、String、Boolean、Object、undefined、null这几种类型。以及涉及到类型判断时会用到的操做有:typeof、instanceof、Object.prototype.toString。
(1)JavaScript中并不区分整型或浮点数,全部数字均统一采用64位浮点数表示。可用Number.MIN_VALUE和Number.MAX_VALUE来获取Number类型的值的范围。
console.log(Number.MIN_VALUE); // 5e-324 console.log(Number.MAX_VALUE); // 1.7976931348623157e+308
(2)NaN
非数值(Not a Number)是一个特殊的数值。当算术运算返回一个未定义或没法表示的值时,就产生NaN了。
console.log(typeof(NaN)); // number console.log(0 / 0); // NaN console.log("hello" / 5); // NaN console.log(NaN + 1); // NaN
注:0除以0会返回NaN。其它数除以0不会。
判断一个变量是不是非数值,可使用isNaN()函数。
console.log(isNaN(NaN)); // true console.log(NaN == NaN); // false console.log(isNaN("hello")); // true,"hello"不能转换成数值 console.log(isNaN(true)); // false console.log(isNaN(false)); // false console.log(isNaN(5)); // false console.log(isNaN("5")); // false
注:NaN与其它全部值都不相等,包括它本身。所以判断NaN不要用
==
或===
。
(3)Infinity(正无穷)和-Infinity(负无穷)
JavaScript中还有两个特殊的数值Infinity和-Infinity。
console.log(typeof(Infinity)); // number console.log(typeof(-Infinity)); // number console.log(1 / 0); // Infinity console.log(1 / -0); // -Infinity
判断一个变量是不是有穷数,可使用isFinite()函数。
console.log(isFinite(Infinity)); // false console.log(isFinite(-Infinity)); // false console.log(isFinite(NaN)); // false console.log(isFinite(0)); // true console.log(isFinite("0")); // true
(1)String类型字符串是由单引号或双引号包括的零个或多个字符序列。
var key = "hello"; var value = 'world';
(2)字符串能够像数组同样访问,但一旦字符串的值肯定了,就不能改变。
var foo = "Java"; foo[0] = "S"; console.log(foo); // Java foo = foo + "Script"; console.log(foo); // JavaScript
(3)toString()与String
var a = 5; var b = false; var c = "AlphaGL" var d = new Date(); console.log(a); // 5 console.log(b); // false console.log(c); // AlphaGL console.log(d); // 2017-05-12T05:54:30.547Z
console.log(String(5)); // 5 console.log(String(false)); // false console.log(null); // null console.log(undefined); // undefined
还有一些常规的String操做的api,能够查阅JavaScript的String api文档。
该类型只有两个字面值:true和false。须要强调的是,true不必定是1,false也不必定是0。其它类型转换成Boolean类型时,有一下规则:
console.log(Boolean("AlphaGL")); // true console.log(Boolean("false")); // true console.log(Boolean("")) // false console.log(Boolean(10)); // true console.log(Boolean(Infinity)); // true console.log(Boolean(-Infinity)); // true console.log(Boolean(0)); // false console.log(Boolean(NaN)); // false console.log(Boolean(new Date())); // true console.log(Boolean(null)); // false console.log(Boolean(undefined)); // false
综上:当值为0,null,false,NaN,undefined,空字符串("")转换成Boolean类型时值都为false,其它任何值都为true。
如下实例:
(1)
var x; if (x) { console.log("test1") }else { console.log("test2") }
输出:test2
(2)
var x = null; if (x) { console.log("test1") }else { console.log("test2") }
输出:test2
(3)
var x = Boolean(false); if (x) { console.log("test1") }else { console.log("test2") }
输出:test2
(4)
var x = false; if (x) { console.log("test1") }else { console.log("test2") }
输出:test2
(5)
var x = new Boolean(false); if (x) { console.log("test1") }else { console.log("test2") }
输出:test1
综上:任何值不为undefined或者null的对象, 包括值为false的Boolean对象, 在条件语句中,其值都将做为true来判断。
对象是JavaScript中重要的数据类型,除数字、true、false、null、undefined和字符串外,全部的值都是对象。在JavaScript中对象是一组无序的键值对集合,相似其它语言的HashMap、Dictionary等数据结构。下面会有单独一小节来专门讲述下对象。
在使用var声明变量但未对其初始化时,该变量的值即为:undefined。undefined类型也只有这一个值。
字面值undefined主要用于比较。
var i = undefined; console.log(i == undefined); // true
未初始化的变量与未声明的变量
var foo; console.log(foo); // undefined console.log(bar); // 报错
var foo; console.log(typeof(foo)); // undefined console.log(typeof(bar)); // undefined
使用typeof操做符来检测未初始化的变量与未声明的变量,都返回undefined。
与undefined相似,null类型也只有一个值null。空值null表示一个空的对象指针。
console.log(typeof(null)); // object
null与undefined比较:
console.log(Number(null)); // 0 console.log(Number(undefined)); // NaN console.log(isNaN(1 + null)); // false console.log(isNaN(1 + undefined)); // true console.log(null === undefined); // false console.log(null == undefined); // true
因为JavaScript是动态类型的,所以就须要一种手段来检测给定的变量的具体数据类型。
(1)typeof
typeof操做符返回的值是如下某个字符串:
console.log(typeof(123)); // number console.log(typeof("123")); // string console.log(typeof(NaN)); // number console.log(typeof(Infinity)); // number console.log(typeof(false)); // boolean console.log(typeof(null)); // object console.log(typeof([])); // object console.log(typeof({})); // object console.log(typeof(function(){})); // function console.log(typeof(undefined)); // undefined
(2)instanceof:下文会讨论。
(3)Object.prototype.toString:下文会讨论。
流程控制主要有条件语句和循环语句。条件语句又分为:if语句,if-else语句,switch语句等。循环语句分为:while循环,for循环,do-while循环。
(1)if语句
if (condition1) { statement1; }else if(condition2) { statement2; }else { statement3; }
该结构,当条件1(condition1为true)知足时,执行语句1(statement1),不然条件2(condition2为true)知足时,执行语句2,以上都不知足则执行语句3(statement3)。
这里的else if
能够有零个或多个,甚至else
都是可选的,根据实际状况来作实际判断。
if (i > 0) { console.log("i大于0"); }else if(i < 0) { console.log("i小于0"); }else { console.log("i等于0"); }
(2)switch语句
switch是一种多分支结构,与if结构相似,也是一种广泛使用的流程控制语句。
switch(expression) { case value1: statement1; break; case value2: statement2; break; default: statement3; }
当表达式(expression)的值,等于对应case的值(value),则会执行对应的语句块(statement),break用于跳出该switch结构,若省略break,则会继续执行下一个case。default用于以上表达式的值都不知足条件。
表达式(expression)可使用任何数据类型,包括字符串、对象。case的值(value)也能够是变量、表达式,不必定是常量。这点与其余有的语言不一样,只能使用数值。
(1)while语句
while循环包含循环条件和循环体,当循环条件知足条件时,才会执行循环体内的代码块。
while(expression) { statement; }
while(i < 100) { i++; }
(2)for语句
for语句通常包含初始条件、循环条件、循环递增(递减)语句三部分。
for(initialization; expression; post-loop-expression) { statement; }
for(var i = 0; i < 100; i++) { console.log(i); }
(3)for-in语句
该语句相似于其余语言的foreach语句,能够用了遍历容器结构或对象的属性。
for (property in expression) { statement; }
var o = [1, 2, 3]; for(var i in o) { console.log(o[i]); }
(4)do-while语句
do-while语句与while语句相似,不一样的时,do-while语句会先执行循环体,再检测循环条件,这就意味着该循环语句至少会执行一次。
do { statement; }while(expression);
(5)break与continue
break和continue均可用于控制循环中代码的执行。
break:当即退出循环。
continue:跳出当前循环,继续执行下次循环。
JavaScript中运算符大致可分为,算术运算符、关系运算符、逻辑运算符以、位运算符以及其它操做符等,固然也包括运算符重载。
+
,-
,*
,/
,%
,++
,--
,+=
,-=
,*=
,/=
,%=
等。<
,>
,<=
,>=
,==
,===
,!=
,!==
。&&
,||
,!
~
,&
,|
,^
,<<
,>>
,>>>
=
,,
,?=
(1)加法操做符(+)
加减法操做符时JavaScript中最经常使用得操做符之一,与其余语言不一样得是,在JavaScript中有一系列特殊得行为,使用时应当注意。
console.log(1 + 2); // 3 console.log(1 + "2"); // 12 console.log("1" + "2"); // 12 console.log(1 + [2]); // 12 console.log(1 + "2" + 3); // 123 console.log("1" + 2 + 3); // 123 console.log(1 + 2 + "3"); // 33 console.log("1" + 2 + "3"); // 123 console.log(1 + [2, 3]); // 12,3 console.log(1 + ""); // 1 console.log(0.1 + 0.2); // 0.30000000000000004 console.log(0.05 + 0.25); // 0.3 // boolean console.log(1 + true); // 2 console.log(1 + false); // 1 // string console.log("AlphaGL:" + null); // AlphaGL:null console.log("AlphaGL:" + undefined); // AlphaGL:undefined console.log("AlphaGL:" + Infinity); // AlphaGL:Infinity console.log("AlphaGL:" + NaN); // AlphaGL:NaN console.log("AlphaGL:" + false); // AlphaGL:false console.log("AlphaGL:" + true); // AlphaGL:true // infinity console.log(1 + Infinity); // Infinity console.log(Infinity + Infinity); // Infinity console.log(1 + (-Infinity)); // -Infinity console.log((-Infinity) + (-Infinity)); // -Infinity console.log(Infinity + (-Infinity)); // NaN // 0 console.log((+0) + (+0)); // 0 console.log((-0) + (-0)); // -0 console.log(0 + (-0)); // 0 // NaN console.log(1 + NaN); // NaN console.log(1 + null); // 1 console.log(1 + undefined); // NaN console.log(1 + (new Date())); // 1Mon May 25 2017 17:09:08 GMT+0800 (中国标准时间) console.log(1 + {name: "AlphaGL"}); // 1[object Object]
综上,使用加法操做符能够总结为以下规则:
- 若是两个操做数都是数值,则执行常规得加法计算。这里须要注意浮点数的加法。
- 若是一个操做数为字符串,则将另一个操做数也转化成字符串类型,再执行字符串的拼接。
- 若是一个操做数是数值,另一个操做是Infinity,则加法结果为Infinity。若是一个操做数是数值,另一个操做数是-Infinity,则加法结果为-Infinity。若是是Infinity加-Infinity,则加法结果为NaN。若是一个操做数是数值,另一个操做数是NaN,则加法结果为NaN。
- 若是一个操做数是数值,另一个操做数是boolean,null类型,则先将boolean和null类型转行成原始值,再执行加法运算。
- 若是一个操做数是数值,另一个操做数是对象,则会先调用对象的
valueOf
方法转化成原始值,若是对象没有valueOf
方法,则调用toString
方法。
(2)减法运算符(-)
减法的运算规则与加法相似,这里就再也不详细介绍了。
(3)乘法运算符(*)
console.log(2 * 3); // 6 console.log(-2 * -3); // 6 console.log(2 * -3); // -6 console.log(2 * Number.MAX_VALUE); // Infinity console.log(-2 * Number.MAX_VALUE); // -Infinity // NaN console.log(2 * NaN); // NaN console.log(-2 * NaN); // NaN console.log(0 * NaN); // NaN console.log(NaN * NaN); // NaN // Infinity console.log(2 * Infinity); // Infinity console.log(-2 * Infinity); // -Infinity console.log(-2 * -Infinity); // Infinity console.log(0 * Infinity); // NaN console.log(Infinity * Infinity); // Infinity console.log(-Infinity * -Infinity); // Infinity // undefined console.log(2 * undefined); // NaN console.log(0 * undefined); // NaN console.log(undefined * undefined); // NaN // boolean console.log(2 * false); // 0 console.log(2 * true); // 2 console.log(2 * "34"); // 68 console.log(2 * "AlphaGL"); // NaN console.log(2 * [3, 4]); // NaN console.log(2 * {name:"34"}); // NaN console.log(2 * new Date()); // 2992421935692
综上,使用减法操做符能够总结为以下规则:
- 两个正数或两个负数相乘,结果为正数。其它有一个操做数为负数,那结果也为负数。若是结果超出数值的表示范围,则结果为Infinity或-Infinity。
- 若是有一个操做数为NaN或undefined,则结果为NaN。
- 若是一个非0数值与Infinity或-Infinity相乘,则结果为Infinity或-Infinity,符号取决于操做数的符号和Infinity仍是-Infinity。0与Infinity或-Infinity,则结果为NaN。
- 若是一个操做数是数值,另一个操做数是boolean或者字符串,则先将该操做数转化为原始值,若是转化后的值不是数值,则结果为NaN,不然执行常规乘法运算。
- 若是一个操做数是数值,另一个操做数是对象,则结果为NaN。若是是Date对象,则乘以基于当前到1970年1月1日起的毫米数。
(4)除法操做数(/)
除法的运算规则与乘法相似,一样,这里就再也不详细介绍了。
(5)模(求余)运算(%)
该运算符是求得除法运算后的余数。
console.log(10 % 3); // 1 console.log(-10 % 3); // -1 console.log(10 % -3); // 1 console.log(10 % 3.14); // 0.5799999999999996
综上,模运算规则以下:
- 模运算的结果的符号,与第一个操做数相同。模运算用于浮点数时,结果会有偏差。
(6)自增(++)与自减(--)
自增和自减有分为前置和后置。
var x = 5; var y = ++x - 2 /* 等价于 * var x = 5; * x = x + 1; * var y = x - 2; */ console.log(x); // 6 console.log(y); // 4
var x = 5; var y = x++ - 2; /* 等价于 * var x = 5; * var y = x - 2; * x = x + 1; */ console.log(x); // 6 console.log(y); // 3
前置自增与后置自增的区别是,前置自增先执行自增,再执行后续的运算,后置自增是先执行运算,再执行自增。同理,自减原理也同样,就不在赘述了。
(7)x op= y操做
这里把+=
,-=
,*=
,/=
,%=
等复合运算统称为op=
,那么:
x op= y
大多数状况下等价于:
x = x op y
其中,下面这个表达式中x计算了两次,在x含有反作用的表达式时,两者就不等价了。
var c = [1, 2, 3]; var i = 0; c[i++] *= 2; console.log(c) // [ 2, 2, 3 ] var d = [1, 2, 3]; var j = 0; d[j++] = d[j++] * 2; console.log(d); // [ 4, 2, 3 ]
用来判断一个操做数是否大于或小于或等于另一个操做数。
console.log(2 < 3); // true console.log("12" < 3); // false console.log("12" < "3"); // true console.log("Alpha" < "alpha"); // true console.log("AlphaGL" < "AlphagL"); // true console.log(2 < "AlphaGL"); // false console.log(2 < true); // false console.log(2 < undefined); // false console.log(2 < null); // false console.log(2 < NaN); // false console.log(false < true); // true console.log(2 < Infinity); // true console.log(2 < -Infinity); // false console.log(Infinity < Infinity); // false console.log(Infinity < Infinity + 1); // false console.log(0 <= 0); // true console.log(0 >= 0); // true console.log(12 == "12"); // true console.log(12 === "12"); // false console.log(12 !== "12"); // true console.log(undefined == 0); // false console.log(undefined == null); // true console.log(undefined == false); // false console.log(null == false); // false console.log(null == 0); // false console.log("" == 0); // true console.log(undefined == ""); // false console.log(2 != NaN); // true console.log(NaN == NaN); // false console.log(NaN != NaN); // true console.log(false == 0); // true console.log(true == 1); // true
综上,关系运算符返回的都是boolean值,有如下规则:
- 若是比较的两个操做数都是数值,则执行数值比较。若是只有一个操做数是数值,则将另一个操做数转换为数值,再执行数值比较。
- 若是比较的两个操做数都是字符串,则依次比较字符串每一个字符的Unicode值。
- 若是有一个操做数是NaN,则执行结果为false,执行不相等操做时,执行结果为true。
- null和undefined相等。但不能将null和undefined转化为其它任何值。
- 若是有一个操做数是对象,另一个操做数不是,则会调用对象的valueOf方法获得原始值,再应用上面的规则。
- 当两个操做数的值相同,类型也相同,而且都不是NaN时,则两个操做数全等(===)。当比较的两个操做数转换为相同类型后的值相等,则两个操做数相等(==)。
(1)逻辑与(&&)
在boolean环境下当逻辑与的两个操做数同时为true时,结果才为true,不然为false。
console.log(new Date() && 2); // 2 console.log(2 && new Date()); // 2017-05-31T02:39:51.033Z console.log(false && new Date()); // false console.log(new Date() && new Date()); // 2017-05-31T02:39:51.035Z console.log(false && 0); // false console.log(true && 0); // 0 console.log(2 && 0); // 0 console.log(2 && ""); // "" console.log(2 && "AlphaGL"); // AlphaGL console.log(2 && null); // null console.log(2 && undefined); // undefined console.log(2 && NaN); // NaN console.log(2 && Infinity); // Infinity
综上,逻辑与的使用规则能够总结以下:
- 若是第一个操做数能转换成false,则返回第一个操做数,不然返回第二个操做数。在boolean环境中使用时,两个操做数结果都为true时,返回true,不然返回false。
- 可以转换为false的值有,0,"",null,undefined。
短路操做:
在执行逻辑与操做时,当第一个操做数的结果为false时,就不在执行第二个操做数的求值了。由于不管第二个操做数为什么值,其结果都不可能为true。
function test(i) { if(i > 0) { return i; }else{ return -i; } } console.log(false && test(2)); // false console.log(true && test(2)); // 2
(2)逻辑或(||)
在boolean环境下当逻辑或的两个操做数任意一个为true时,结果都为true。通常,可用来给变量设置默认值。
console.log(new Date() || 2); // 2017-05-31T02:46:51.732Z console.log(2 || new Date()); // 2 console.log(false || new Date()); // 2017-05-31T02:48:51.732Z console.log(new Date() || new Date()); // 2017-05-31T02:48:51.732Z console.log(false || 0); // 0 console.log(true || 0); // true console.log(2 || 0); // 2 console.log(2 || ""); // 2 console.log(2 || "AlphaGL"); // 2 console.log(2 || null); // 2 console.log(2 || undefined); // 2 console.log(2 || NaN); // 2 console.log(2 || Infinity); // 2
综上,逻辑或的使用规则能够总结以下:
- 若是第一个操做数能转换成true,则返回第一个操做数,不然返回第二个操做数。在boolean环境中使用时,两个操做数任意一个为true时,返回true,不然返回false。
- 可以转换为false的值有,0,"",null,undefined。
短路操做:
在执行逻辑或操做时,当第一个操做数的结果为true时,就不在执行第二个操做数的求值了。由于不管第二个操做数为什么值,其结果都不可能为false。
function test(i) { if(i > 0) { return i; }else{ return -i; } } console.log(false || test(2)); // 2 console.log(true || test(2)); // true
(3)逻辑非(!)
不管操做数是什么类型的数据,该操做都会返回一个boolean。逻辑非会先将操做数转换为一个boolean,再对齐求反。
console.log(!0); // true console.log(!""); // true console.log(!NaN); // true console.log(!null); // true console.log(!undefined); // true console.log(!Infinity); // false console.log(!2); // false console.log(!"AlphaGL"); // false console.log(!new Date()); // false
综上,逻辑非的使用规则能够总结以下:
- 若是操做数能转换为true的话,则返回false,不然返回false。
- 可以转换为false的值有,0,"",null,undefined。
位运算是比较低层次的运算,按内存中表示数值的位来操做数值。JavaScript中全部的数值都是以64位格式存储,而位操做符是先将64位的值转换成32位的整数,而后执行操做,最后再将结果转换回64位。
对于有符号的整数,32中的前31位表示整数的值,第32位表示数值的符号,用0表示整数,1表示负数,所以第32位也叫符号位。其中,正数是以二进制格式存储的,负数二进制补码的形式存储的。
(1)原码、反码和补码
原码,是该数值的符号位与绝对值的二进制表示。例如:
2[原码]: 0000 0000 0000 0000 0000 0000 0000 0010 -2[原码]: 1000 0000 0000 0000 0000 0000 0000 0010
反码,正数的反码是其原码。负数的反码,是符号位不变,其他各位取反,即1变成0,0变成1。例如:
2[反码]: 0000 0000 0000 0000 0000 0000 0000 0010 -2[反码]:1111 1111 1111 1111 1111 1111 1111 1101
补码,正数的补码是其原码。负数的补码,是其反码加1。例如:
2[补码]: 0000 0000 0000 0000 0000 0000 0000 0010 -2[补码]:1111 1111 1111 1111 1111 1111 1111 1110
(2)按位与(&)
按位于,是将两个操做数的二进制位对齐,当两个数值的位都为1时,结果为1,任意一个为0,则结果为0。
console.log(3 & 5); // 1 3 = 0000 0000 0000 0000 0000 0000 0000 0011 5 = 0000 0000 0000 0000 0000 0000 0000 0101 & = 0000 0000 0000 0000 0000 0000 0000 0001
(3)按位或(|)
按位或,是将两个操做数的二进制位对齐,当两个数值的位任意一个为1时,结果为1,两个都为0,则结果为0。
console.log(3 | 5); // 7 3 = 0000 0000 0000 0000 0000 0000 0000 0011 5 = 0000 0000 0000 0000 0000 0000 0000 0101 | = 0000 0000 0000 0000 0000 0000 0000 0111
(4)按位非(~)
按位非,是获得该数值的反码。
console.log(~3); // -4 3 = 0000 0000 0000 0000 0000 0000 0000 0011 ~ = 1111 1111 1111 1111 1111 1111 1111 1100
(5)按位异或(^)
按位异或,是将两个操做数的二进制位对齐,当两个数值的位其中只有一个为1时,结果为1,两个都为0或都为1,则结果为0。
console.log(3 ^ 5); // 6 3 = 0000 0000 0000 0000 0000 0000 0000 0011 5 = 0000 0000 0000 0000 0000 0000 0000 0101 ^ = 0000 0000 0000 0000 0000 0000 0000 0110
(6)左移(<<)
左移,是将操做数的全部位移动指定的位数,右侧多出的位用0填充。左移不影响操做数的符号位。
console.log(3 << 2); // 12 console.log(-3 << 2); // -12 3 = 0000 0000 0000 0000 0000 0000 0000 0011 << 2 = 0000 0000 0000 0000 0000 0000 0000 1100
(7)有符号右移(>>)
有符号右移,是将操做数的全部位移动指定的位数,并保留符号位。左侧多出的位用0填充。
console.log(12 >> 2); // 3 console.log(-12 >> 2); // -3 12 = 0000 0000 0000 0000 0000 0000 0000 1100 >> 2 = 0000 0000 0000 0000 0000 0000 0000 0011
(8)无符号右移(>>>)
无符号右移,是将操做数的全部位移动指定的位数。对于正数,无符号右移与有符号右移结果相同,负数会以补码的形式右移指定的位。
console.log(12 >>> 2); // 3 console.log(-12 >>> 2); // 1073741821
(1)赋值运算符(=)
赋值能够和其余运算符组合使用。例如:
var x = 3; console.log(x += 5); // 8
(2)逗号运算符(,)
逗号运算符,能够再一条语句中执行多个操做。若是,逗号运算符用于赋值,则返回表达式中的最后一项。
var x = 2, y = 3, z = 5; var pos = (2, 3, 5); console.log(z); // 5 console.log(pos); // 5
(3)三目运算符(?=)
三目运算符,格式形如:
variable = boolean_expression ? true_value : false_value
当表达式boolean_expression的值位true时,则返回true_value的值,不然,返回false_value的值。
console.log(1 > 2 ? 1 + 2 : 1 - 2); // -1
在介绍数据类型的时候提到过,在JavaScript中对象是一组无序的键值对集合,相似其它语言的HashMap、Dictionary等数据结构。除数字、true、false、null、undefined和字符串外,全部的值都是对象。JavaScript内置了Object、Date、Array、Function、RegExp等对象。全部对象继承Object对象。
对象的建立分为两种方式:
(1)使用new操做符,后面紧跟构造函数
var student = new Object(); // 等价于 var student = {}; student.name = "AlphaGL"; student.age = 18; student.print = function () { console.log("hello AlphaGL"); }
(2)使用对象字面量表示法。由若干名/值对中间用冒号分割,每对名/值对间用逗号分隔组成的映射表。
var student = { name : "AlphaGL", age : 18 print: function () { console.log("hello AlphaGL"); }, };
能够经过点(.)或者中括号([])的方式获取对象属性的值。
(1)经过点(.)来获取
var student = { name : "AlphaGL", age : 18 }; console.log("name = " + student.name); // name = AlphaGL
(2)经过中括号访问属性的值,中括号内能够是变量且计算结果必须是字符串的表达式。若是属性名包含回致使语法错误的字符,或者属性名使用的是关键字或者保留字,也可使用中括号表示。
var name = "nick name"; student[name] = "AlphaGL"; // 等价于 student["nick name"] = "AlphaGL";
通常推荐使用点的方式去获取对象属性。
(1)hasOwnProperty()方法能够检测给定属性存在于对象实例中时,则返回true。
function Student() { } Student.prototype.work = "game"; var stu = new Student(); stu.name = "AlphaGL"; stu.age = 18; console.log(stu.hasOwnProperty("name")); // true console.log(stu.hasOwnProperty("work")) // false
(2)in操做符会访问对象的给定属性,不管该属性是存在于实例中仍是原型中都返回true。
function Student() { } Student.prototype.work = "game"; var stu = new Student(); stu.name = "AlphaGL"; stu.age = 18; console.log("name" in stu); // true console.log("work" in stu) // true
delete运算符能够用来删除对象的自有属性,不会删除原型的同名属性,删除不存在的属性在对象上,delete将不会起做用,但仍会返回true。成功删除的时候会返回true,不然返回false。
function Student() { }; Student.prototype.name = "hello"; var stu = new Student(); stu.name = "AlphaGL"; stu.age = 18; console.log(delete stu.name); // true console.log(delete stu.name); // 什么不作,一样返回true console.log(stu.name); // hello
JavaScript中,数组算是最经常使用的类型。数组的大小能够动态调整,每一项能够保存任何类型的数据,起始项从0开始。还能够实现堆栈,队列等数据结构。
(1)数组的建立
使用Array构造函数建立。
var nums = new Aarray(3); var names = new Array("foo", "bar") var colors = Array("R", "G", "B")
使用数组字面量表示法。即便用中括号,并将每一个元素用逗号隔开。
var num = [1, 2, 3]; var names = ["foo", "bar"]; var params = [1.2, "ab", true]; var pos = [{x:1, y:2}, {x:3, y:4}];
(2)数组元素的访问。
var a = ["AlphaGL", 18, true]; console.log(a[0]); // AlphaGL console.log(a[1]); // 18 console.log(a[2]); // true //indexOf返回在数组中能够找到一个给定元素的第一个索引,若是不存在,则返回-1。相似的还有lastIndexOf方法。 console.log(a.indexOf("AlphaGL")); // 0 console.log(a.indexOf(true)); // 2 console.log(a.indexOf(18)); // 1 console.log(a.indexOf(2)); // -1 console.log(a.length); // 3 console.log(a[3]); // undefined。javascript数组下标从0开始。
可使用负数或非整数来索引数组。这时,数值将会转换为字符串,而该索引被看成对象的常规属性。若是,使用了非负整数的字符串,则它会被看成数组索引访问,而不是对象属性访问。
var a = ["AlphaGL", 18, true]; console.log(a[-1]); // undefined console.log(a[1.5]); // undefined console.log(a["1"]); // 18 console.log(a["2"]); // true
由此可知,数组的访问只是对象访问的一种特殊形式,当访问不存在的属性时,javascript也不会报错,只会返回undefined值。所以,javascript中数组不存在数组越界的问题。
(3)数组元素的添加与删除
添加元素:
var a = []; a[0] = "AlphaGL"; a.push(18, true); console.log(a); // [ 'AlphaGL', 18, true ]
删除元素:
var a = ["AlphaGL", 18, true]; delete a[1]; console.log(a[1]); // undefined console.log(a.length); // 3 console.log(a.pop()); // true。从数组中删除最后一个元素,并返回该元素的值 console.log(a.length); // 2 console.log(a.shift()) // AlphaGL。从数组中删除第一个元素,并返回该元素的值 console.log(a.length); // 1 a[0] = undefined; console.log(a.length); // 1
var a = ["AlphaGL", 18, true]; a.length = 2; console.log(a[2]); // undefined a.length = 0; console.log(a[0]); // undefined
var a = ["AlphaGL", 18, true]; a.splice(2, 0, "haha"); // 从第2个元素开始,删除0个元素,即添加元素haha console.log(a); // [ 'AlphaGL', 18, 'haha', true ] a.splice(1, 2); // 从第1个元素开始,删除2个元素。 console.log(a); // [ 'AlphaGL', true ] a.splice(0, 1, "haha"); // 从第0个元素开始,删除1个元素,并添加haha元素。 console.log(a); // [ 'haha', true ]
注:删除数组元素与将数组元素赋值为undefined值相似。使用delete不是修改数组的length属性,也不会移动后继元素位置。其它操做方法基本都会移动数组元素和改变数组length值。也能够直接操做数组的length属性来达到输出元素的目的。push和pop方法提供了相似栈结构的操做。push和shift方法则提供了相似队列结构的操做。splice有替换数组中任意数量的项的做用。
(4)数组的检测
var a = ["AlphaGL", 18, true]; console.log(Array.isArray(a)); // true console.log(a instanceof Array); // true
注:当存在两个以上的全局执行环境时,即存在两个以上不一样版本的Array构造函数,instanceof则只能在单一的全局执行环境有效。
(5)数组的排序
var a = [1, 11, 57, 7, 23]; a.sort(function (p1, p2) { // 使用比较函数来对数组元素进行排序。返回的值小于0,则p1放到p2位置以前;大于0则p1在p2以后;等于0则位置不变。 return p1 > p2; }); console.log(a); // [ 1, 7, 11, 23, 57 ]
var a = ["AlphaGL", 18, true]; a.reverse(); // 逆序数组。 console.log(a); // [ true, 18, 'AlphaGL' ]
(6)数组的遍历与迭代
var a = [1, 11, 57, 7, 23]; var t1 = a.every(function (element, index, array) { return element % 2 != 0; }); var t2 = a.every(function (element, index, array) { return element > 10; }); console.log(t1); // true console.log(t2); // false
注:every方法会对数组中的每一项运行给定函数,若是该函数的每一项都返回true,则结果才为true。
var a = [1, 11, 57, 7, 23]; var t1 = a.filter(function (element, index, array) { return element % 2 != 0; }); var t2 = a.filter(function (element, index, array) { return element > 10; }); console.log(t1); // [ 1, 11, 57, 7, 23 ] console.log(t2); // [ 11, 57, 23 ]
注:filter方法会对数组中的每一项运行给定的函数,并返回该函数会返回为true的项组成的新数组。
var a = [1, 11, 57, 7, 23]; var t1 = a.forEach(function (element, index, array) { array[index] = element + 1; }); console.log(a); // [ 2, 12, 58, 8, 24 ]
注:forEach方法一样会对数组中每一项运行给定的函数。该方法没有返回值。
var a = [1, 11, 57, 7, 23]; var t1 = a.map(function (element, index, array) { if(element > 10) { return element + 1; } return element - 1; }); console.log(t1); // [ 0, 12, 58, 6, 24 ]
注:map方法会将每次运行给定的函数返回的值,组成一个新的数组。
var a = [1, 11, 57, 7, 23]; var t1 = a.some(function (element, index, array) { return element > 50; }); console.log(t1); // true
注:map方法一样会对数组中的每一项都运行给定的函数,若是该函数的任一项结果为true,则返回true。
(7)其它
固然,数组还有一些其它的用法和函数。这里就不一一介绍了。感兴趣的,能够参考文末列举的参考连接。
函数,简单描述就是可重复调用屡次的功能模块。在JavaScript中,每一个函数都是Function类型的实例,所以也同样具备属性和方法。函数名也是对象,能够把函数看成值来使用,这样就提供极大的灵活性。
在JavaScript中,函数的定义有以下几种实现方式:
(1)function关键字+函数名+参数列表+花括号构成的语句块,例如:
function foo(p1, p2) { return p1 + p2; } console.log(typeof(foo)); // function console.log(foo(3, 4)); // 7
(2)使用Function构造函数。通常,不推荐这种使用方法。
var foo = new Function("p1", "p2", "return p1 + p2"); console.log(foo(3, 4)); // 7
(3)函数表达式
// 声明了一个匿名函数,并赋值给foo变量。 var foo = function(p1, p2) { return p1 + p2; } console.log(foo(3, 4)); // 7 // 函数表达式也能够包含名称 var bar = function sum(p) { if(p <= 1) { return 1; }else { return p + sum(p - 1); } } console.log(bar(5)); // 15 // 声明即调用 var sum = function(p1, p2) { return p1 + p2; }(3, 4); console.log(sum); // 7
JavaScript中函数定义并未指定函数参数的类型,调用时也未对实参的值作类型检查,一样也不检查参数个数。
function foo(p1, p2, p3) { return p2; } console.log(foo(1)); // undefined console.log(foo(1, 2)); // 2 console.log(foo(1, 2, 3)); // 2 console.log(foo(1, 2, 3, 4)); // 2
当形参与实参的个数不匹配时,少的参数将被置为undefined,多的参数将被丢弃。
在函数内部,有个特殊的对象arguments。该对象用来保存函数参数,能够像数组样使用数字索引来访问参数,一样它也包含length属性。但它并非真正的数组。另外,该对象还包含callee属性,该属性指向拥有这个arguments对象的函数。
function foo(p1, p2, p3) { console.log(arguments.length); // 3 console.log(arguments[0]); // 第一个参数,即:1 console.log(arguments[1]); // 第二个参数,即:2 console.log(arguments[2]); // 第三个参数,即:3 } foo(1, 2, 3);
使用arguments和callee:
function sum(p) { if (p <= 1) { return 1; }else { return p + arguments.callee(p -1); } } console.log(sum(5)); // 15
前面提到过,每一个函数都是Function类型的实例,所以也同样具备属性和方法。函数有如下比较经常使用的属性。
function foo(p1, p2 , p3) { console.log(arguments.length); console.log(arguments.callee.length); } console.log(foo.name); // foo console.log(foo.length); // 3 foo(1, 2); // 2 3
由上可知:
foo.name:函数的名称。
foo.length:形参的个数。
arguments.length:实参的个数。
arguments.callee.length:形参的个数。
闭包(closure)是函数型语言的一个重要的特性,许多高级特性都依赖闭包来实现。闭包,是建立在一个函数内部的函数,可以访问函数内部的变量,并保存在内存中,记录上次运行的结果,即保存了建立时的上下文环境信息。所以,能够简单总结为:
闭包=函数内部建立的函数 + 该函数建立时的上下文环境信息
例如:
function counter() { var count = 0; return function() { return count++; } } var foo = counter(); console.log(foo()); // 0 console.log(foo()); // 1 console.log(foo()); // 2
闭包的这种机制,就实现面向对象的封装提供了支持。将私有变量封装在内部,提供外包接口函数,来访问该变量。
构造函数
函数内部属性
函数的做用域
reduce
前面提到过,JavaScript中全部的都是对象。在面向对象编程中,对象是类的实例,而类是具备相同属性和行为的一类对象的抽象和集合。例如:狮子对象是动物这一类型中的一个实例。面向对象编程有三大特性:封装,继承和多态。
前面提到过,使用new关键字调用构造函数能够建立一个新对象。
function Student(name, age) { this.name = name; this.age = age; this.setName = function(n) { this.name = n; } this.getName = function() { return this.name; } } var student = new Student("张三", 18); student.setName("李四"); console.log(student.getName()); // 李四
其中,this关键字指向了,当前要建立的对象。
每一个对象都有一个私有属性(prototype)原型,该属性指向该对象的原型对象。能够理解为其余编程语言中的,指向基类或者父类的做用。固然,该原型对象一样有一个本身的prototype,层层向上直到该对象的原型为null。null没有原型。JavaScript中几乎全部的对象都是位于原型链顶端的Object的实例,一样能够理解为,都是Object的子类。所以,使用原型对象可让全部对象实例共享它所包含的属性和方法。
function Student(name, age) { this.name = name; this.age = age; this.getName = function() { return this.name; } } var student1 = new Student("张三", 18); var student2 = new Student("李四", 18); console.log(student1.getName == student2.getName); // false
上面,建立两个不一样的对象实例,getName实现了相同的功能,却每一个对象中都保留了一份,形成没必要要的浪费。这就须要经过原型prototype来解决此问题了。
function Student(name, age) { this.name = name; this.age = age; Student.prototype.getName = function() { return this.name; } } Student.prototype.country = "china"; var student1 = new Student("张三", 18); var student2 = new Student("李四", 18); console.log(student1.getName == student2.getName); // true console.log(student1.country); // china console.log(student2.country); // china
function A() { } A.prototype.name = "小明"; A.prototype.age = 18; A.prototype.country = "china"; function B() { } B.prototype = new A(); B.prototype.name = "小李"; B.prototype.age = 20; function C() { } C.prototype = new B(); var c = new C(); c.name = "小赵"; c.country = "shanghai"; console.log(c.country); // shanghai console.log(c.age); // 20 console.log(c.name); // 小赵
当访问对象的某个属性时,会根据给定的属性名称来查找。若是,在该对象的实例中找到该属性,则返回属性的值;不然,则继续查找该对象的原型对象,在原型对象中查找该属性,依次层层向上搜索,直到搜索到原型链的末尾。所以,对象的属性会覆盖同名的该对象的原型对象的同名属性。
function A() { } function B() { } var a1 = new A(); console.log(a1 instanceof A); // true console.log(a1 instanceof B); // false console.log(A.prototype.isPrototypeOf(a1)); // true console.log(B.prototype.isPrototypeOf(a1)); // false A.prototype = {}; var a2 = new A(); console.log(a1 instanceof A); // false console.log(a2 instanceof A); // true console.log(A.prototype.isPrototypeOf(a1)); // false console.log(A.prototype.isPrototypeOf(a2)); // true B.prototype = new A(); var a3 = new B(); console.log(a3 instanceof A); // true console.log(a3 instanceof B); // true console.log(B.prototype.isPrototypeOf(a3)); // true console.log(A.prototype.isPrototypeOf(a3)); // true
经过以上实例能够总结以下:
object instanceof constructor
运算符,用来检测 constructor.prototype是否存在于参数object的原型链。虽然,右操做数是构造函数,但实际上检测了对象的继承关系,而不是检测建立对象的构造函数。
prototypeObj.isPrototypeOf(object)
检查一个对象是否存在于另外一个对象的原型链上。能够理解为object对象是否继承自prototypeObj.prototype。