介绍
这是些零散的知识点,整理整理。涉及ES5/ES6/ES7。
1、数据类型 / 转换
JavaScript
默认内置其中数据类型:
6种原始类型:null
,undefined
, boolean
,number
,string
和ES6新出的symbol
;
1种其他类型:object
。1
2
3
4
5
6typeof 0 // number
typeof true // boolean
typeof 'Leo' // string
typeof Math // object
typeof null // object
typeof Symbol('Leo') // symbol (New ES6)
①对比 null 和 undefined
undefined
表示尚未定义,常作为变量,函数参数,对象属性的默认值。当函数没有返回值时也会默认返回undefined
。null
表示空值,常被赋值给一个变量表示没有值
。
②隐式转换类型
1 | let leo = 'leo' |
此时字符串变量 leo
会被转换成 true
,并执行后面的代码。Falsy
类型的值,指在强制类型转换时会被转成布尔 false
的值。包括: ""
,0
,null
,undefined
,NaN
,false
。
除了 Falsy
类型的值,其他都成为 truthy
类型的值,并转换成 true
。1
2
3
4
5
6Boolean(null) // false
Boolean('leo') // true
Boolean('0') // true
Boolean(' ') // true
Boolean([]) // true
Boolean(function(){}) // true
③String 和 Number 转换
+
既可以作为算术运算也可以连接字符串。
但是 *
,/
和-
与字符串一起使用,会强制将字符串转换成数字类型。1
2
3
4
5
6
7
8
9
10
11
121 + "2" = "12"
"" + 1 + 0 = "10"
"" - 1 + 0 = -1
"-9\n" + 5 = "-9\n5"
"-9\n" - 5 = -14
"2" * "3" = 6
4 + 5 + "px" = "9px"
"$" + 4 + 5 = "$45"
"4" - 2 = 2
"4px" - 2 = NaN
null + 1 = 1
undefined + 1 = NaN
④== 和 ===
==
比较强制类型转换后的结果,而 ===
是直接比较。1
2
3
41 == '1' // True
1 === '1' // False
undefined == null // True
undefined === null // False
强制类型转换会造成一些混乱,比如:1
2
3
4
5
6
7
8let a1 = '0',a2 = false,a3 = 0;
Boolean(a1) // True
Boolean(a2) // False
Boolean(a3) // False
a1 == a2 // True
a1 == a3 // True
a1 === a2 // False
a1 === a3 // False
即:其他类型
与 Boolean
对比,会先把 其他类型
转换成 Boolean类型
再比较;string类型
与 Number
对比,会先把 string类型
转换成 Number类型
再比较;
还有一些特殊的比较:1
2
3
4
5
6
7
8
9false == "" // true
false == [] // true
false == {} // false
"" == 0 // true
"" == [] // true
"" == {} // false
0 == [] // true
0 == {} // false
0 == null // false
2、函数表达式 和 函数声明
函数表达式只有被执行后才可用,它不会被提升(相当于赋值函数表达式给变量):1
2
3let leo = function(a,b){
return a + b ;
}
函数声明则可以在定义前后被任意调用,因为它始终会被提升:1
2
3function leo(a,b){
return a + b;
}
3、var let const
var
在某一函数内部声明的变量和方法只能在其函数作用域内部访问到。let
所声明的变量,不会被提升,只在 let
命令所在的代码块内有效。const
所声明的变量,不能被重新赋值,但其指向的值是可以被操作的。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23function funvar(){
if(true){
var a = '这是funvar'
}
console.log(a);
}
function funlet(){
if(true){
let b = '这是funlet'
}
console.log(b);
}
funvar(); // 这是funvar
funlet(); // <b is not defined>
const leo = 'my name is leo~';
leo = 'try to change name';
console.log(leo) // TypeError: Assignment to constant variable.
const leoArr = ['hello','my','name','is','leo'];
console.log(loeArr[0]); // ['hello','my','name','is','leo']
leoArr[0] = 'Hi';
cconsole.log(leoArr) // ["Hi", "my", "name", "is", "leo"]
4、上下文
上下文的概念经常会同作用域之间混淆。为了保持条理清晰,我们需要注意以下两条:
上下文是在函数被调用时确定的。它通常指的是你的代码当中某一部分的值。
作用域值的则是变量能过被访问到的范围。
1 | // 作用域 |
例如上述实例当中调用 this
的位置不同,this
的指向也是不同的,也就表示着不同的上下文;而作用域则是我们在编写代码时使用 var
关键字来确定的。
5、函数调用方式:call apply bind
这三种方法可以改变函数调用时 this 的指向,区别则在于函数调用的时候。
.call() 会立即调用函数,并要求你按次序一个个传入参数。
.apply() 也会立即调用函数,不过你需要以数组的形式传参。
.call() 和 .apply() 效用几乎是相同的,它们都可以用来调用对象中的某个方法,具体怎么使用取决于你的使用场景里如何传参更方便。
1 | const Snow = {username: 'Snow'} |
注意: 如果你在使用 .call() 时传入了数组形式的参数,它会把整个数组当作一个参数使用。
不过在 ES6 里你倒是可以试试展开操作符的方法进行传参:1
char.knows.call(Snow, ...["nothing", "Jon"]); // You know nothing, Jon Snow
.bind()
不会直接触发某个方法,而是根据你传入的参数和上下文返回一个新的方法。
当你想要在程序之后的某些上下文环境中调用一个方法时可以使用 .bind()
这种方式。
这在我们使用一些异步操作或者事件处理函数时非常有用。.bind()
的传参形式则类似于 .call()
你需要讲参数以逗号分隔传入:1
2
3
4
5
6
7
8const Snow = {surename: 'Snow'}
const char = {
surename: 'Stark',
knows: function(arg, name) {
console.log(`You know ${arg}, ${name} ${this.surename}`);}
}
const whoKnowsNothing = char.knows.bind(Snow, 'nothing');
whoKnowsNothing('Jon'); // You know nothing, Jon Snow
5、严格模式
我们可以通过使用 "use strict"
指令来启用 JavaScript
的严格模式。它会为你的代码添加更多的限制及错误处理。
使用严格模式的好处有:
更方便调试 你能够看到更多的报错,例如在你试图为只读的全局对象或属性赋值时。
防止意外产生全局变量 对未声明的变量进行赋值时会报错。
禁止无效的删除操作 尝试删除变量、函数、不可删除的属性时会报错。
禁止重复的属性名及参数 如果有命名重复的属性名或者参数值就会报错。
让eval()
的调用更加安全 在eval()
方法内部定义的变量及函数不会污染其他作用域。
禁止this
指向全局对象 当this
的值为null
或者undefined
时不会再默认指向到全局对象。这也就意味着在函数内部的this
不会再默认指向window
对象了。
6、Object.keys()
Object.keys()
方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for...in
循环遍历该对象时返回的顺序一致 (两者的主要区别是 一个 for-in 循环还会枚举其原型链上的属性)。
详细:点击查看
🌰栗子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23/* Array 对象 */
let arr = ["a", "b", "c"];
console.log(Object.keys(arr)); // ['0', '1', '2']
/* Object 对象 */
let obj = { foo: "bar", baz: 42 },
keys = Object.keys(obj); // ["foo","baz"]
/* 类数组 对象 */
let obj = { 0 : "a", 1 : "b", 2 : "c"};
console.log(Object.keys(obj)); // ['0', '1', '2']
// 类数组 对象, 随机 key 排序
let anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj)); // ['2', '7', '100']
/* getFoo 是个不可枚举的属性 */
var my_obj = Object.create(
{},
{ getFoo : { value : function () { return this.foo } } }
);
my_obj.foo = 1;
console.log(Object.keys(my_obj)); // ['foo']
7、”void”运算符
查看原文 MDN介绍
这个运算符能向期望一个表达式的值是undefined
的地方插入会产生副作用的表达式。
void 运算符通常只用于获取undefined
的原始值,一般使用void(0)
(等同于void0
)。在上述情况中,也可以使用全局变量undefined
来代替(假定其仍是默认值)。
在MDN上的定义是:void运算符
对给定的表达式进行求值,然后返回undefined
。
使用它来实现立即执行的函数表达式(IIFE
),如下:1
2
3
4
5
6
7
8
9void function foo(){
console.log('hello Leo')
}()
// 与下面等效
(function foo(){
console.log('hello Leo')
})()
使用void
必须注意的是,无论给定表达式返回结果如何,void运算符
整体返回的结果都是空的(undefined
)!1
2
3
4
5
6
7const w1 = void function foo(){
return 'hello leo'
}() // undefined
const w2 = (function foo(){
return 'hello leo'
})() // hello
与 async
一起使用,这样就能把函数作为异步代码的入口:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21void async function (){
try{
const res = await fetch('air.ghost.io');
const text = await res.text();
console.log(text);
} catch(e){
console.log(e)
}
}
// 与下面等效
(async ()=>{
try{
const res = await fetch('air.ghost.io');
const text = await res.text();
console.log(text);
} catch(e){
console.log(e);
}
})();
另外,经常也用在 URI操作
上,当用户点击一个以 javascript: URI
时,它会评估URI中的代码,然后用返回的值替换页面内容,除非返回的值是undefined
。void运算符
可用于返回undefined
。例如:1
2
3
4
5
6
7
8<a href="javascript:void(0);">
这个链接点击之后不会做任何事情,如果去掉 void(),
点击之后整个页面会被替换成一个字符 0。
</a>
<p> chrome中即使<a href="javascript:0;">也没变化,firefox中会变成一个字符串0 </p>
<a href="javascript:void(document.body.style.backgroundColor='green');">
点击这个链接会让页面背景变成绿色。
</a>
注意,虽然这么做是可行的,但利用 javascript:
伪协议来执行JavaScript
代码是不推荐的,推荐的做法是为链接元素绑定事件。
8、简单介绍闭包
闭包
,又一个老生常谈的话题,可以用一句话对之概括:有权访问另一个函数作用域内变量的函数都是闭包。例如:1
2
3
4
5
6
7
8
9
10function outer(){
var a = 0;
function inner(){
console.log(a++);
};
return inner;
}
var closure = outer();
closure(); // 1
closure(); // 2
这里返回的inner函数
就是能够访问outer函数
中变量的闭包,除inner函数
之外的外部作用域都无法访问outer函数
中的变量a
。
闭包特性:
- 1.函数返回嵌套的函数形成闭包
- 2.闭包内部可以访问外部的参数和变量
- 3.外部参数和变量在被闭包引用时不会被垃圾回收机制回收
闭包优点:
- 1.可避免变量对全局的污染
- 2.允许函数私有成员的存在
- 3.允许变量长驻内存
闭包缺点:
由于变量常驻内存,增大内存使用量,使用不当很容易造成内存泄漏。
闭包应用场景:
- 1.采用函数引用方式的setTimeout调用
- 2.将函数关联到对象的实例方法
- 3.封装相关的功能集