1.基础语法
1.1变量
1.1.1 var的使用
声明
使用 var 操作符(注意 var 是一个关键字),后跟变量名,它保存任何类型的值(不初始化的情况下,变 量会保存一个特殊值 undefined)。使用var不仅可以改变变量保存的值,也可以改变值的类型
作用域
使用 var 在一个函数内部定义一个变量,就意味着该变量将在函数退出时被销毁
function test() {
var message = "hi"; // 局部变量
}
test();
console.log(message); // 出错!
不过,在函数内定义变量时省 略 var 操作符,可以创建一个全局变量,只要调用一次函数 test(),就会定义 这个变量,并且可以在函数外部访问到
function test() {
message = "hi"; // 全局变量
}
test();
console.log(message); // "hi"
var声明提升(仅var有,let、const没有)
“提升”(hoist),也就是把所有变量声明都拉到函数作用域的顶部。使用这个关键字声明的变量会自动提升到函数作用域顶部,if、for不属于函数作用域
function foo() {
console.log(age);
var age = 26;//var会被提升到函数作用域顶部
}
foo(); // undefined
//代码等同于
function foo() {
var age;
console.log(age);
age = 26;
}
foo(); // undefined
使用var声明同一个变量也没有问题。
1.1.2 let的使用
声明
let 跟 var 的作用差不多,但有着非常重要的区别。最明显的区别是,let 声明的范围是块作用域, 而 var 声明的范围是函数作用域。
let声明仅在块作用域中有效,例如 if for { }中,
if (true) {
let age = 26;
console.log(age); // 26
}
//块作用域外部
console.log(age); // ReferenceError: age 没有定义
//不同作用域之间
let age = 30;
console.log(age); // 30
if (true) {
let age = 26;
console.log(age); // 26
}
let 也不允许同一个块作用域中出现冗余声明。对声明冗余报错不会因混用 let 和 var 而受影响。用var和let声明同一个变量名会报错
暂时性死区
let 与 var 的另一个重要的区别,就是 let 声明的变量不会在作用域中被提升。
// name 会被提升
console.log(name); // undefined
var name = 'Matt';
// age 不会被提升
console.log(age); // ReferenceError:age 没有定义
let age = 26;
全局变量
与 var 关键字不同,使用 let 在全局作用域中声明的变量不会成为 window 对象的属性(var 声 明的变量则会)。
let依旧可以声明成为全局变量,只是在顶层对象window中不存在,区分var使用
var name = 'Matt';
console.log(window.name); // 'Matt'
let age = 26;
console.log(window.age); // undefined
for循环中的使用
在使用 var 的时候,最常见的问题就是对迭代变量的奇特声明和修改:
for (var i = 0; i < 5; ++i) {
setTimeout(() => console.log(i), 0)
}
// 你可能以为会输出 0、1、2、3、4
// 实际上会输出 5、5、5、5、5
之所以会这样,是因为在退出循环时,迭代变量保存的是导致循环退出的值:5。在之后执行超时 逻辑时,所有的 i 都是同一个变量,因而输出的都是同一个最终值。Javascript引擎是在执行完for循环后再依次执行for循环中的语句,如果使用var定义,for循环结束后 i 的值为5,所以依次执行setTimeout输出的是五个5
for (let i = 0; i < 5; ++i) {
setTimeout(() => console.log(i), 0)
}
// 会输出 0、1、2、3、4
而在使用 let 声明迭代变量时,JavaScript 引擎在后台会为每个迭代循环声明一个新的迭代变量。 每个 setTimeout 引用的都是不同的变量实例,所以 console.log 输出的是我们期望的值,也就是循 环执行过程中每个迭代变量的值。
1.1.3 const的使用
声明
const 的行为与 let 基本相同,唯一一个重要的区别是用它声明变量时必须同时初始化变量,且 尝试修改 const 声明的变量会导致运行时错误。
1.1.4 使用优先
因为在ES6中引入了块作用域的概念,所以优先使用let和const,在知道变量不需要更改的时候选择使用const,在知道未来需要修改时使用let
1.2 数据类型
1.2.1 基本数据类型
javascript中有7种数据类型 Undefined、Null、Boolean、Number、String、Symbol(ES6新增)、Object
1.2.2 typeof操作符
let message = "some string";
console.log(typeof message); // "string"
console.log(typeof(message)); // "string"
console.log(typeof 95); // "number"
//第一个message是 字符串“message” ,第二个是变量message
在这个例子中,我们把一个变量(message)和一个数值字面量传给了 typeof 操作符。注意,因 为 typeof 是一个操作符而不是函数,所以不需要参数(但可以使用参数)
1.2.3 Undefined
Undefined 类型只有一个值,就是特殊值 undefined。当使用 var 或 let 声明了变量但没有初始 化时,就相当于给变量赋予了 undefined 值
Undefined 是一个假值。
1.2.4 null
null 类型同样只有一个值,即特殊值 null。逻辑上讲,null 值表示一个空对象指针,这也是给 typeof 传一个 null 会返回"object"的原因
null 是一个假值。
1.2.5 Boolean
要将一个其 他类型的值转换为布尔值,可以调用特定的 Boolean()转型函数
数据类型 | 转换为true | 转换为false |
---|---|---|
Boolean | true | false |
String | 非空字符串 | 空字符串 |
Number | 非0 | 0、NaN |
Object | 任意对象 | null |
Undefined | 不存在转换为true的情况 | Undefined |
1.2.6 Number
浮点值
因为存储浮点值使用的内存空间是存储整数值的两倍,所以 ECMAScript 总是想方设法把值转换为 整数。在小数点后面没有数字的情况下,数值就会变成整数。类似地,如果数值本身就是整数,只是小 数点后面跟着 0(如 1.0),那它也会被转换为整数,如下例所示
let floatNum1 = 1.; // 小数点后面没有数字,当成整数 1 处理
let floatNum2 = 10.0; // 小数点后面是零,当成整数 10 处理
计算浮点数时存在精度问题
计算一些浮点数时,javascript会 采用IE754转换成二进制,然后再进行计算,计算完成后再将结果转换成10进制,因此存在四舍五入的问题。
解决方案 先把小数转成整数.在计算, 最后把结果再转换成小数就ok了
function MathTool() {
//获取小数长度
var getDecimalsLength = function (num) {
return num.toString().split(".")[1] ? num.toString().split(".")[1].length : 0
}
//获取最大长度
var getMaxLength = function (num1, num2) {
var num1Length = getDecimalsLength(num1);
var num2Length = getDecimalsLength(num2);
var p = Math.max(num1Length, num2Length);
var times = Math.pow(10, p);
return times
}
//加法
this.add = function(num1, num2) {
var times = getMaxLength(num1, num2);
return (this.mul(num1 ,times) + this.mul(num2 ,times)) / times;
}
//减法
this.sub = function(num1, num2) {
var times = getMaxLength(num1, num2);
return (this.mul(num1 ,times) - this.mul(num2 , times)) / times;
}
//乘法
this.mul = function(num1, num2) {
var times = getMaxLength(num1, num2);
var intNum1 = num1.toString().replace(".", "")
var intNum2 = num2.toString().replace(".", "")
var countDecimals = getDecimalsLength(num1) + getDecimalsLength(num2)
return intNum1 * intNum2 / Math.pow(10, countDecimals);
}
//除法
this.div = function(num1, num2) {
var times = getMaxLength(num1, num2);
return (this.mul(num1 , times) / this.mul(num2 , times));
}
}
var MathTool = new MathTool();
console.log(MathTool.add(0.1, 0.2))
console.log(MathTool.sub(0.0012, 0.0002))
值的范围(Infinity)
ECMAScript 可以表示的最小 数值保存在 Number.MIN_VALUE 中,这个值在多数浏览器中是 5e324;可以表示的最大数值保存在 Number.MAX_VALUE 中,这个值在多数浏览器中是 1.797 693 134 862 315 7e+308。如果某个计算得到的 数值结果超出了 JavaScript 可以表示的范围,那么这个数值会被自动转换为一个特殊的 Infinity(无 穷)值。任何无法表示的负数以-Infinity(负无穷大)表示,任何无法表示的正数以 Infinity(正 无穷大)表示。
NaN
有一个特殊的数值叫 NaN,意思是“不是数值”(Not a Number),用于表示本来要返回数值的操作 失败了(而不是抛出错误)。比如,用 0 除任意数值在其他语言中通常都会导致错误,从而中止代码执 行。但在 ECMAScript 中,0、+0 或 -0 相除会返回 NaN
如果分子是非 0 值,分母是有符号 0 或无符号 0,则会返回 Infinity 或-Infinity
任何涉及 NaN 的操作始终返回 NaN(如 NaN/10),在连续多步计算 时这可能是个问题。其次,NaN 不等于包括 NaN 在内的任何值
(NaN == NaN)// false
isNaN() 该函数接收一个参数,可以是任意数据类型,然后判断 这个参数是否“不是数值”。把一个值传给 isNaN()后,该函数会尝试把它转换为数值。某些非数值的 值可以直接转换成数值,如字符串"10"或布尔值。任何不能转换为数值的值都会导致这个函数返回 true。
console.log(isNaN(NaN)); // true
console.log(isNaN(10)); // false,10 是数值
console.log(isNaN("10")); // false,可以转换为数值 10
console.log(isNaN("blue")); // true,不可以转换为数值
console.log(isNaN(true)); // false,可以转换为数值 1
数值转换
Number()、parseInt()和 parseFloat()
1.2.7 String
字符串可以使用双引号(")、 单引号(’)或反引号(`)标示,因此下面的代码都是合法的
几乎所有值都有的 toString()方法。这个方法唯 一的用途就是返回当前值的字符串等价物
注意模板字符串的 ` ` 调用,在模板中应用${ } 插入js语句
1.2.8 Symbol(ES6新增)
声明
//普通声明
const level1 = Symbol();
console.log(level1) //Symbol()
//在声明中定义
const level2 = Symbol("level");
console.log(level1) //Symbol("level")
//描述相同不代表两个对象相同
const level3 = Symbol("level")
console.log(level3 == level4)//false
使用Symbol() 添加对象属性
const level = Symbol("level")
const student = {
name:'xiaoming',
age:12,
[level]:"A"
}
//for in 遍历对象不能获取Symbol
for(let pro in student){
console.log(pro);//name,age
}
console.log(student[level]);//A
Symbol.iterator
如果对象有 Symbol.iterator这个属性,这个对象就可以被for…of遍历
1.2.9 Object
ECMAScript 中的对象其实就是一组数据和功能的集合。对象通过 new 操作符后跟对象类型的名称 来创建。开发者可以通过创建 Object 类型的实例来创建自己的对象,然后再给对象添加属性和方法
constructor:用于创建当前对象的函数。在前面的例子中,这个属性的值就是 Object() 函数。
hasOwnProperty(propertyName):用于判断当前对象实例(不是原型)上是否存在给定的属 性。要检查的属性名必须是字符串(如 o.hasOwnProperty(“name”))或符号。
isPrototypeOf(object):用于判断当前对象是否为另一个对象的原型。(第 8 章将详细介绍 原型。)
propertyIsEnumerable(propertyName):用于判断给定的属性是否可以使用(本章稍后讨 论的)for-in 语句枚举。与 hasOwnProperty()一样,属性名必须是字符串。 toLocaleString():返回对象的字符串表示,该字符串反映对象所在的本地化执行环境。
toString():返回对象的字符串表示。
valueOf():返回对象对应的字符串、数值或布尔值表示。通常与 toString()的返回值相同。
1.2.10 操作符
递增递减
无论使用前缀递增还是前缀递减操作符,变量的值都会在语句被求值之前改变。(在计算机科学中, 这通常被称为具有副作用。)请看下面的例子
let age = 29;
let anotherAge = --age + 2;
console.log(age); // 28
console.log(anotherAge); // 30
后缀版递增和递减在语句被求值后才发生
let num1 = 2;
let num2 = 20;
let num3 = num1-- + num2;
let num4 = num1 + num2;
console.log(num3); // 22
console.log(num4); // 21
String和Number相加
Number数值会转换成String类型
let result1 = 5 + 5; // 两个数值
console.log(result1); // 10
let result2 = 5 + "5"; // 一个数值和一个字符串
console.log(result2); // "55"
== 和 === 的区别
==会将数据类型进行转换
=== 不会将数值类型进行转换
let result1 = ("55" == 55); // true,转换后相等
let result2 = ("55" === 55); // false,不相等,因为数据类型不同
for…in…
for-in 语句是一种严格的迭代语句,用于枚举对象中的非符号键属性
语法如下: for (property in expression) statement
for-in用于遍历对象
for…of…
for-of 语句是一种严格的迭代语句,用于遍历可迭代对象的元素
语法如下: for (property of expression) statement
for-of用于遍历数组