概念
- JavaScript的组成
- 核心———— ECMAScript
- 文档对象模型———— DOM
- 浏览器对象模型———— BOM
- W3C(World Wide Web Consortium,万维网联盟):负责制定 Web 通信标准
- ECMA-262第6版,俗称ES6、ES2015或ES Harmony;ES6正式支持了类、模块、迭代器、生成器、箭头函数期约、反射、代理和众多的数据类型
在HTML中使用
区分< script >属性
用于在HTML页面中插入JavaScript;可在页面嵌入或外部引入
特点:
- 代码执行顺序:< script > 元素内部js代码从上到下依次解释;
- 阻塞页面:< script > 元素内部js代码执行完毕,页面才会被浏览器加载或显示,也就是说,解析JavaScript代码或文件时,页面的处理也会暂时停止
- 顺序:浏览器按照< script > 元素在页面出现的先后顺序对它们依次进行解析,在第一个 < script >元素内宿包含的代码解析完成后,第二个< script >包含的代码才会解析,然后是第三个、第四个……(包含defer、async属性除外)
- 位置:一般放在 body 元素中页面内容的后面(因为按照传统做法放在 head 标签中,会导致浏览器页面出现明显的延迟)
属性:
- async :异步脚本——立即下载脚本,不保证先后顺序,脚本之间互不依赖。页面不需要等待脚本下载和执行,从而异步加载页面其他内容。建议异步脚本不要再加载期间修改DOM
- *charset : 指定字符集
- crossorigin:配置相关请求的cors(跨域资源共享)设置。默认不使用cors。crossorigin=”anonymous”配置文件请求不必设置凭据标志。crossorigin=”use-credentials”设置怕凭据标志,意味着出站请求会包含凭据。
- defer : 延迟脚本——脚本延迟至整个页面完全被解析和显示之后再执行,脚本执行时不会影响页面的构造——立即下载,延迟执行
- integrity:允许比对接收到的资源和指定的加密前面以验证子资源完整性(SRI,
Subresource Integrity)。如果接收到的资源的签名与这个属性指定的签名不匹配,则页面会报错,
脚本不会执行。这个属性可以用于确保内容分发网络(CDN,Content Delivery Network)不会提
供恶意内容。 - src : 外部文件链接
- *type : 脚本语言类型(MIME类型);默认为 text/javascript;如果这个值是module,则代码会被当成ES6模块,而且只有这种时候代码中才能出现import和export关键字。
行内代码与外部文件
比起行内代码,使用外部文件来包含JavaScript代码有以下优点:
- 可维护性
- 可缓存(如果有两个页面都使用同一个文件,那么这个文件只需被下载一次)
- 适应未来
< noscript >
不支持JavaScript时浏览器显示的内容,例:
<noscript>
<p>本页面需要浏览器支持(启用)JavaScript。</p>
</noscript>
语言基础
语法
-
注意点:区分大小写、函数名不能使用关键字、推荐使用驼峰命名、
第一个字符必须是一个字母 下划线( _ )或一个美元符号($) -
严格模式: 在脚本顶部添加代码
“use strict”;
关键字和保留字
若用关键字作标识符,会导致 “Identifier Expected” 错误
变量
ECMAScript 的变量是 松散类型 的,所谓松散类型就是可以用来保存任何类型的数据。
换句话说,每个变量仅仅是一个用于保存值的占位符而已。
给未经声明的变量赋值 在严格模式下会抛出 ReferenceError 错误。
有三个关键字可以声明变量:var、const和let;var适用于ECMAScript所有版本,const和let只适用于ECMAScript6及更晚的版本。
var 关键字:
- 用于定义变量,不限类型
- 不初始化的情况下,变量会保存一个特殊值 undefined
- 作用域:var定义的是包含它的函数的的局部变量,该变量将在函数退出时被销毁
- 在函数内定义变量省略var操作符,会创建一个全局变量,最好不要这样做,不利于维护
- 声明提升:使用var声明的变量会自动提升到函数作用域的顶部;反复多次使用var声明同一个变量也没问题
let 声明:
let与var的区别:
- let声明的范围是块作用域,而var是函数作用域(块作用域是函数作用域的子集);
- let不可在同一块作用域中重复声明同一个变量;
- let声明的变量不会提升
- let在全局作用域中声明的变量不会成为window对象(let声明仍然是在全局作用域中发生的)
- for循环中:使用var定义的迭代变量会渗透到循环体外部;使用let定义的迭代变量的作用域仅限于for循环体内部
const声明:
const与let的区别:
- cosnt在声明时必须初始化变量,且不能修改
- 不能用const来声明迭代变量
- const声明的限制只适用于它指向的变量的引用。换句话说,如果const变量引用的是一个对象,那么修改这个对象内部的属性并不违反const的限制
变量的最佳实践: const优先,let次之,不使用var
数据类型
6+1 = 7 种
ES6 基本数据类型(6个): Number, String, Boolean, null, undefined, symbol (ES6新增)
引用数据类型:
object
typeof 操作符
用于鉴定给定变量的数据类型
但无法完全区分 null 和 object,调用 typeof 都返回“object”;
因为特殊值 null 被认为是一个空的对象的引用
typeof "some string"; //"string"
typeof 95; //"number"
typeof undefined; // "undefined"
typeof true; // "boolean"
function Fun(){ alert('函数') };
typeof Fun; //"function"
// 特殊
var obj = {a:1};
typeof obj; // "object"
typeof null; // "object"
Number
-
浮点数值
var floatNum1 = 0.1;
var floatNum2 = .1; // 有效,不推荐
保存浮点数值需要的 内存空间 是保存整数值的两倍;所以ES总是想方设法的把值转换为整数对于极大或绩效的数值,可用 e 表示法(科学计数法)
var floatNum = 3.125e7 // 等于31250000(3.125*10ⁿ,n=7)
浮点的最高精度是17位小数,算数计算时其精确度不如整数。例如,0.1加0.2的结果不是0.3,而是0.30000000000000004,所以:if(a+b == 0.3){ // 不要做这样的测试!
alert(“You got 0.3.”)
} -
数值范围
由于内存的限制,ECMAScript并不能保存所有的数值
大多数浏览器支持的数值范围在 5e-324 ~ 1.7976931348623157e+308 之间,超过则会被转换为 Infinity,此时无法再进行计算。
isFinite() 用以判断是否在正常范围内
-
NaN ( Not a Number )
一个特殊的数值,用于表示一个本来要返回数值的操作数未返回数值的情况(这样就不会抛出错误了)。
NaN不等于包括NaN在内的任何值console.log(NaN == NaN); // false
isNaN(a) 用于判断a是否“不是数值”
-
数值转换
Number()
Number(true) // 1 Number(false) // 0 Number(null) // 0 Number(undefined) // NaN Number('011') // 11 Number('Hello') // NaN //特殊:如果参数是包含十六进制格式如“0xf”的字符串,则会转换为与该十六进制值对应的十进制整数值
parseInt()
parseInt("1234abc") // 1234 parseInt("22.5") // 22 parseInt("0xf") //15 parseInt("0xAF",16) // 175
可接收两个参数,第一个需要转换的对象,第二个转换成多少进制的数值;不传第二个参数可根据数值形式自动转换
区别:Number()对空字符串返回0,parseInt()返回NaN
parseFloat()
与parseInt()类似,是对浮点数进行转换,直到第二个小数点失效;并且只解析 十进制 值(十六进制值始终会返回0)。
String
以双引号(””)、单引号(”)或反引号(“)表示的由零或多个16位Unicode字符组成的字符序列
-
字符字面量
转义序列,用于表示非打印字符,或者具有其他用途的字符。
一般用 \ 表示字面量 含义 \n 换行 \t 制表 \b 退格 \r 回车 \f 换页 \ \ 反斜杠(\) \ ‘ 单引号 \ “ 双引号 \ ` 反引号 \ xnn 以十六进制代码nn表示的一个字符(其中n为0 ~ F)。例如,\x41表示”A” \unnnn 以十六进制代码nnnn表示的一个Unicode字符,其中n为0 ~ F。例如,\u03a3表示希腊字符 ∑ -
转换为字符串
toString() 不适用于null和undefined;
多数情况下,调用toString()方法不必传参数;
可传一个参数,用以表示输出数值的底数,默认十进制,比如:
let num = 10; alert(num.toString()); // "10" alert(num.toString(2)); // "1010" alert(num.toString(8)); // "12"
String() 适用于null和undefined;
let value1 = null; alert(String(value1)); //"null"
Boolean
true false
转型函数 Boolean(),可对任意数据类型调用该函数,且都会返回一个Boolean值
Boolean('some string'); // true
Boolean(""); // false
Boolean(1); // true
Boolean(50); //true
Boolean(0); // false
Boolean(NaN); // false
Boolean(window); // true (任何对象)
Boolean(null); // false
Boolean(undefined); // false
在运用流语句(如 if 语句),会自动执行相应的 Boolean 转换
null
空对象指针
一般用于 定义的变量用于 保存对象,则最好将该变量初始化为null而不是其他值
typeof null; // 'object'
null == undefined; // true (undefined的值是派生于null值的)
undefined
表已声明变量但未对其加以初始化
symbol
表示一个独一无二的值
创建:
const a = Symbol();
const b = Symbol(); //任何人在任何作用域内都无法重新创建初这个值
console.log(a); //Symbol(), 此时得到了一个在内存中独一无二的值
尽管a和b都是使用Symbol()创建出来的,但是它们在内存中看起来却是这样的
a!==b; //a和b是两块内存的引用
const c = a; // 把a里保存的地址保存在c变量值
a === c; // 指向同一块内存
这种行为与对象遵循相同的规则,如
var a = {}
var b = {}
a!==b; //a和b各自被分配了不同的内存,因此它们保存了不同的地址
var c = a; //借助变量a,变量c拿到了a指向的那个对象的地址,因此两者相等
a === c;
但是对于同为基本数据类型的字符串来说,它不遵循类似的规则,如
两者指向内存中同一块内存地址
不能使用new关键字:
const a = new Symbol(); //报错, Symbol is not a constructor
Symbol类型的值也不参与运算
可以接收一个字符串参数便于区分
//使用场景
1. 作为属性名,可以保证属性不重名
2. 定义常量
cloud.tencent.com/developer/a…
object
一组数据和功能的集合
可通过 new 来创建对象实例
var o = new Object()
Object的每个实例都有下列属性和方法:
constructor:保存着用于创建当前对象的函数(构造函数)
hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(而非在实例的原型中)是否存在;作为参数的属性名必须以字符串的形式指定
isPrototypeOf(object):用于检查传入的对象是否是传入对象的原型
propertyIsEnumerable(propertyName):用于检查给定的属性是否能用 for-in 来枚举;作为参数的属性名必须以字符串的形式指定
toLocaleString():返回对象的字符串表示,该字符串与执行环境地区对应
toString():返回对象的字符串表示
valueOf():返回对象的字符串、数值或布尔值表示;通常与toString()方法的返回值相同
ECMAScript中Object是所有对象的基础,因此所有对象都具有这些基本的属性和方法
操作符
操作数据值
一元操作符
只操作一个值
1. 递增和递减
// 前置
var age = 29;
++age; // age等于30,改变了age值,相当于 age = age + 1
--age; // 29
当语句中包含其他操作时,变量的值是在语句被求值以 前 改变的。例
var age = 29;
var anotherAge = --age + 2;
alert(age); // 28
alert(anotherAge); //30
// 后置
var age = 29;
age++; //30
当语句中包含其他操作时,变量的值是在语句被求值以 后 改变的。例
var num1 = 2;
var num2 = 20;
var num3 = num1-- + num2; // 等于 22
var num4 = num1 + num2; // 等于 21
总结:
a. 当一个语句中递增或递减时唯一一个操作时,前置和后置都适用;如果包含其他操作,最好用前置
b. 递增递减操作还可用于字符串、布尔值、浮点数值和对象,过程中可能涉及类型的转换
2. 一元加和减操作符
var num = 25;
num = +num; // 25
一元加放在数值前面不会对结果产生任何影响,但是
放在非数值类型前面,相当于用了Number()来转型,例
var s1 = "01";
var s2 = "1.1";
var s3 = "z";
var b = false;
var f = 1.1;
var o = {
valueOf: function() { return -1; }
};
s1 = +s1; // 值变成数值 1
s2 = +s2; // 值变成数值 1.1
s3 = +s3; // 值变成 NaN
b = +b; // 值变成数值 0
f = +f; // 值未变,仍然是 1.1
o = +o; // 值变成数值-1
一元减主要用于表负数
var num = 25;
num = -num; //-25
一元减应用于非数值时,执行与一元加相同的规则,再转换为负数
位操作符( 了解 )
位操作符用于在最基本的层次上,即按内存中表示数值的位来操作数值。
布尔操作符
测试两个值的关系
1. 逻辑非 ( ! )
逻辑非操作符首先会将它的操作数转换为一个布尔值,然后再 对其求反
两个感叹号(!!)就是“负负得正”
2. 逻辑与 ( && )
第一个操作数为false,就不会对第二个操作数求值
3. 逻辑或 ( || )
第一个操作数为false,才会对第二个操作数求值
乘性操作符
乘法* 、 除法/ 、 求模(余数)%
加性操作符
操作数值的加法或减法
操作加法时,如果其中一个操作数是字符串,则进行字符串的拼接;
如果有一个操作数是对象、数值或布尔值,则调用它们的 toString()方法取得相应的字符串值, 然后再应用前面关于字符串的规则。对于 undefined 和 null,则分别调用 String()函数并取得字符 串”undefined”和”null”。
每个加法操作是独立进行的
关系操作符
< 、 > 、 <= 、 >=
进行两个操作数的数值或字符编码值的比较,期间可能涉及到类型转换
注:
- 比较英文单词时,可将两个单词都转为小写形式,得出更为正确的结果(大写字母的字符编码全部小于小写字母的字符编码);
- 比较数字字符串时,需保证至少一个是 number 类型;
- 任何操作数与 NaN 进行比较,结果都是 false;
相等操作符
相等 == ; 不相等 !=
全等 === ;不全等 !==
相等和不相等是需强制转型的,全等和不全等比较的是未转型的操作数
null == undefined; // true
null === undefined; // false
条件操作符
三目运算符
var a = trueOrFalse ? b : c ;
赋值操作符
=
复合赋值(为了简化赋值操作)
*=、 /=、%=、+=、-= …
逗号操作符
一条语句中执行多个操作
var num1=1, num2=2, num3=3;
语句
if语句
if(…) … else …
循环语句
do-while
后测试循环语句,即在对条件表达式求值之前,循环体内的代码至少会被执行一次
do {
statement
} while ( expression )
常用于循环体中代码至少要被执行一次的情况
while
前测试循环语句,先对条件求值,满足条件则进入循环体,不满足则不执行循环体
while ( expression ) statement
for
前测试循环语句;
可在循环前初始化变量、定义循环后要执行的代码
for (initialization; expression; post-loop-expression) statement;
// 这三个表达式可以省略;
// 若全部省略 for(;;){...},则为一个无限循环;若只给出了控制表达式 for(;i<count;){...},则转换成了while循环
示例:
var count = 10;
for (var i = 0; i < count; i++){
alert(i)
}
使用 while 循环做不到的,使用 for 循环同样也做不到;for 循环只是把与循环有关的代码集中在了一个位置。
for-in
精准的迭代语句;可以用来枚举对象的属性
for (poperty in expression) statement
示例:
for (var propName in window) {
document.write(propName)
}; // 循环显示window对象的所有属性
注:因为ECMAScript对象的属性没有顺序,所以 for-in 循环输出的属性名的顺序是不可预测的
label语句
一般与for循环语句配合使用,用以跳出循环(多发生在循环嵌套的情况下)
label: statement
例:
start: for( let i = 0; i < 10; i++){
for(let j = 0; j < 10; j++){
if(i === 5 && j === 5){
break start;
}
}
};
// 此例在i=5和j=5时跳出整个循环;
// 该例中的 start 标签可以在将来由 break 和 continue 语句引用
break 和 continue 语句
都用于在循环中精确地控制代码的执行
break 立即退出循环
continue 立即退出循环,但退出循环后会从循环的顶部继续执行
with 语句
用以简化多次编写同一对象的工作,例
var qs = location.search.substring(1);
var hostName = location.hostName;
var url = location.href;
// 以上代码都包含location对象,可用with语句改写:
with(location){
var qs = search.substring(1);
var hostName = hostName;
var url = href;
}
// with 语句的代码块内部,每个变量首先被认为是一个局部变量,而如果在局部环境中找不到该变量的定义,就会查询location对象中是否有同名的属性;如果发现了同名的属性,则以location对象属性的值作为变量的值
// 严格模式下不允许使用
// 大量使用with语句会导致性能下降,所以不建议使用
switch 语句
类似 if 语句的流控制语句
switch (expression) {
case value: statement
break;
case value: statement
break;
case value: statement
break;
case value: statement
break;
default: statement
}
// switch语句在比较值时使用的是全等操作符,因此不会发生类型转换
函数
封装 >> 调用
使用 function 关键字来声明,后跟一组参数以及函数体
function funName(arg0, arg1, ..., argN){
statements
};
可通过 return 语句实现返回值 (会直接停止函数运行)
理解参数
ECMAScript 中的参数在内部是用 一个数组 来表示的;在函数体内可通过 arguments 对象来访问这个参数数组
function sayHi(name,message){
alert("Hello " + name + ", " + message)
};
// 等同于
function sayHi(){
alert("Hello " + arguments[0] + "," + arguments[1]);
};
这说明 命名的参数只提供便利,但不是必需的
arguments的值永远与对应命名参数的值保持同步,但两个值的内存空间是独立的,即
function doAdd(num1, num2) {
arguments[1] = 10; // 严格模式下不适用(不能直接赋值)
alert(arguments[0] + num2);
};
// 修改了 arguments[1], 也修改了 num2
变量、作用域与内存
原始值与引用值
-
概念:
原始值:按值访问
引用值:保存在内存中的对象。保存引用值的变量是按引用(by reference)访问的。
JavaScript不允许直接访问内存位置,因此也就不能直接操作对象所造的内存空间。在操作对象时,实际上操作的是对该对象的引用(reference)而非实际的对象本身。
-
动态属性:
只有引用值能添加动态属性;如果使用的是new关键字,则JavaScript会创建一个Object类型的实例,但其行为类似原始值。
-
复制值:
原始值的复制会形成两个独立的变量,互不干扰。
引用值的复制也会形成两个变量,但复制的值实际上是一个指针,它指向存储在堆内存中的对象。操作完成后,两个变量实际上指向同一个对象,因此一个对象上面的变化会在另一个对象上反映出来。
let obj1 = new Object();
let obj2 = obj1;
obj1.name = "Nicholas";
console.log(obj2.name); // "Nicholas
如图所示:
- 传递参数
ECMAScript中所有函数的参数都是按值传递的(传递对象参数时也是如此,不能因为在函数内部设置属性的变化反映到了函数外部的对象,就任务对象参数时按引用传递的)。如:
function setName(obj) {
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
let person = new Object();
setName(person);
console.log(person.name); // "Nicholas"
// 函数中参数的值改变之后,原始的引用仍然没变;
// 当obj在函数内部被重写时,它变成了一个指向本地对象的指针,这个本地对象在函数执行结束时就被销毁了。
ECMAScript中函数的参数就是局部变量。
-
确定类型
原始值用typeof
引用值用instanceof,例
person instanceof Object
执行上下文与作用域
内部上下文可以通过作用域链访问外部上下文中的一切,但外部上下文无法访问内部上下文中的任何东西。
作用域链增强
有两种情况,会将作用域链前端临时添加一个上下文,这个上下文在代码执行后会被删除
- try/catch语句中的catch块
- with语句
变量声明
使用var的函数作用域声明
使用var声明变量时,变量会被自动添加到最接近的上下文。
存在“声明提升”
使用let的块级作用域声明
注意
-
如果变量未经声明就被初始化了,那么它就会自动被添加到全局上下文。(未经声明而初始化变量是JavaScript编程中一个非常常见的错误,会导致很多问题。为此,在初始化变量前一定要先声明变量。在严格模式下,未经声明就初始化变量会报错。)