js之综合


基本类型和引用类型

基本类型:存放在栈中。引用类型:同时保存在栈内存和堆内存中,在堆中存放了对象,在栈中存放了指针。

typeof可以区分几种

不能区分null和Object,typeof对于基本类型,除了null都可以区分,对于对象,除了函数都显示object

1
2
3
4
5
6
7
8
typeof Symbol(); // symbol 有效
typeof ''; // string 有效
typeof 1; // number 有效
typeof true; //boolean 有效
typeof undefined; //undefined 有效
typeof new Function(); // function 有效
typeof null; //object 无效
typeof [] ; //object 无效

类型相互转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
1、toString()
// null和undefined会报错
// 数值
let num = 10
num.toString() // '10'
num.toString(2) // '1010',二进制
// 数组
let arr = [1,2,3]
let arr2 = [{a: 1}, {b: 2}]
arr.toString() // '1,2,3'
arr2.toString() //"[object Object],[object Object]"
// 对象
let obj = {a: 1}
obj.toString() // "[object Object]"

2、String()
数组和对象都一样,null和undefined会转为'null', 'undefined'

3、Number()
// 数值
Number(3.15); //3.15
Number(023); //19
Number(0x12); //18
// 字符串
Number('324') // 324
Number('324abc') // NaN
Number('') // 0
// 特殊
Number(null) // 0
Number(undefined) // NaN
// 对象
Number({a: 1}) // NaN
Number([1, 2, 3]) // NaN
Number([5]) // 5

四则运算符

只有当加法运算时,其中一方是字符串类型,就会把另一个也转为字符串类型。其他运算只要其中一方是数字,那么另一方就转为数字。

1
2
3
4
5
6
1 + '1' // '11'
2 * '2' // 4
[1, 2] + [2, 1] // '1,22,1'
// [1, 2].toString() -> '1,2'
// [2, 1].toString() -> '2,1'
// '1,2' + '2,1' = '1,22,1'

作用域

作用域就是一个独立的地盘,让变量不会外泄、暴露出去,不同作用域下同名变量不会有冲突。ES6 之前 JavaScript只有全局作用域和函数作用域。ES6的到来,为我们提供了‘块级作用域’。

未定义直接赋值的变量会自动声明为拥有全局作用域
1
2
3
4
5
6
7
function outFun2() {
variable = "未定义直接赋值的变量";
var inVariable2 = "内层变量2";
}
outFun2();//要先执行这个函数,否则根本不知道里面是啥
console.log(variable); //未定义直接赋值的变量
console.log(inVariable2); //inVariable2 is not defined
作用域链
1
2
3
4
5
6
7
8
var a = 100
function fn() {
var b = 200
console.log(a) // 这里的a在这里就是一个自由变量
console.log(b)
}
fn()
// 如果父级也没呢?再一层一层向上寻找,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是 作用域链 。

执行上下文

执行上下文就是当前 JavaScript 代码被解析和执行时所在环境的抽象概念, JavaScript 中运行任何的代码都是在执行上下文中运行

执行上下文的类型

1、全局执行上下文:1. 创建一个全局对象,在浏览器中这个全局对象就是 window 对象。2. 将 this 指针指向这个全局对象。一个程序中只能存在一个全局执行上下文。
2、函数执行上下文:每次调用函数时,都会为该函数创建一个新的执行上下文。
3、Eval 函数执行上下文: 运行在 eval 函数中的代码也获得了自己的执行上下文,一般不常用。

执行上下文的生命周期

1、创建阶段:当函数被调用,但未执行任何其内部代码之前,做以下3件事:
创建变量对象:首先初始化函数的参数arguments,提升函数声明和变量声明。
创建作用域链:作用域链用于解析变量。当被要求解析变量时,JavaScript 始终从代码嵌套的最内层开始,如果最内层没有找到变量,就会跳转到上一层父作用域中查找,直到找到该变量。
确定this指向。
2、执行阶段:执行变量赋值、代码执行。
3、回收阶段:执行上下文出栈等待虚拟机回收执行上下文。

变量提升

函数声明和变量声明都会提升。函数声明优先级比较高,因此变量声明会被函数声明所覆盖,但是可以重新赋值。

1
2
3
4
5
6
7
8
9
function test(arg){
console.log(arg); // 函数声明比变量声明优先级高,所以是function(){ console.log('hello world') }
var arg = 'hello';
function arg(){
console.log('hello world')
}
console.log(arg); // arg = 'hello';后赋值,所以打印为'hello'
}
test('hi');
this指向
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 情况1:对于直接调用 foo 来说,不管 foo 函数被放在了什么地方,this 一定是 window
function foo() {
console.log(this.a) //1
}
var a = 1
foo()

// 情况2:对于 obj.foo() 来说,我们只需要记住,谁调用了函数,谁就是 this,所以在这个场景下 foo 函数中的 this 就是 obj 对象
function fn(){
console.log(this);
}
var obj={fn:fn};
obj.fn(); //this->obj

// 情况3:在构造函数模式中,类中(函数体中)出现的this.xxx=xxx中的this是当前类的一个实例
function CreateJsPerson(name,age){
//this是当前类的一个实例p1
this.name=name; //=>p1.name=name
this.age=age; //=>p1.age=age
}
var p1=new CreateJsPerson("尹华芝",48);

// 情况4:call、apply和bind:this 是第一个参数
function add(c, d){
return this.a + this.b + c + d;
}
var o = {a:1, b:3};
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34

// 情况5:箭头函数this指向:箭头函数没有自己的this,看其外层的是否有函数,如果有,外层函数的this就是内部箭头函数的this,如果没有,则this是window。
<button id="btn1">箭头函数this</button>
<script type="text/javascript">
let btn1 = document.getElementById('btn1');
let obj = {
name: 'kobe',
age: 39,
getName: function () {
btn1.onclick = () => {
console.log(this);//obj
};
}
};
obj.getName();
1
2
3
4
alert(a);//function a(){ alert('我是函数') }
function a(){ alert('我是函数') }
var a = '我是变量';
alert(a); //'我是变量'
执行上下文栈
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var color = 'blue';
function changeColor() {
var anotherColor = 'red';
function swapColors() {
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
}
swapColors();
}
changeColor();
// 当上述代码在浏览器中加载时,JavaScript 引擎会创建一个全局执行上下文并且将它推入当前的执行栈,放在栈的底部
// 调用 changeColor函数时,此时changeColor函数内部代码还未执行,js执行引擎立即创建一个changeColor的执行上下文,然后把这执行上下文压入到执行栈中,放到全局执行上下文的上面。
// 执行changeColor函数过程中,调用swapColors函数,同样地,swapColors函数执行之前也创建了一个swapColors的执行上下文,并压入到执行栈中,放在changeColor上面。
swapColors函数执行完成,swapColors函数的执行上下文出栈,并且被销毁。
changeColor函数执行完成,changeColor函数的执行上下文出栈,并且被销毁。

如何区分数组、对象4种方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let arr = [1,2,3]
let obj = {a: 1}
let fn = function(){return 222}
1、用constructor判断
arr.constructor === Array
obj.constructor === Object
fn.constructor === Function

2、用instanceof判断
console.log(arr instanceof Array);
console.log(obj instanceof Object);
console.log(fn instanceof Function);

3、Object.prototype.toString()
Object.prototype.toString.call(arr) === '[object Array]'
Object.prototype.toString.call(obj) === '[object Object]'
Object.prototype.toString.call(fn) === '[object Function]'

4、识别所有类型
Object.prototype.toString.call(null) === '[object Null]'
Object.prototype.toString.call(undefined) === '[object Undefined]'
Object.prototype.toString.call(1) === '[object Number]'
Object.prototype.toString.call('12') === '[object String]'
Object.prototype.toString.call(false) === '[object Boolean]'
Object.prototype.toString.call(new Date()) === '[object Date]'
Object.prototype.toString.call(/\d/g) === '[object RegExp]'

深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
function deepClone (obj) {
if (obj.constructor !== Array && obj.constructor !== Object) return false
let result = obj instanceof Array ? [] : {}
for(let key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]
}
}
return result
}

// 第二种
(function (result){
let types = 'Object Array Function'.split(' ')
function type () {
return Object.prototype.toString.call(this).slice(8, -1)
}
types.forEach(item => {
result[`is${item}`] = function (self) {
return type.call(self) === item
}
})
})(window.judgeType || (window.judgeType = {}))

function copy (obj) {
if (judgeType.isArray(obj) || judgeType.isObject(obj)) {
let target = judgeType.isArray(obj) ? [] : {}
for (let key in obj) {
target[key] = copy(obj[key])
}
}
if (judgeType.isFunction(obj)) {
return evel((function () {obj + ''})())
}
return obj
}