内置类型
JS中分为七种内置类型,七种内置类型又分为两大类型:基本类型和对象(Object)
基本类型有六种:null, undefined, boolean, number, string, symbol。
其中JS的数字类型是浮点类型的,没有整形。并且浮点类型基于IEEE 754标准实现,在使用中会遇到某些BUG。 NaN 也属于 number 类型,并且NaN 不等于自身。 NaN == NaN //false
对于基本类型来说,如果使用字面量的方式,那么这个变量只是个字面量,只有在必要的时候才会转换为对应的类型。
let a = 111 //字面量,不是number 类型
Object 是引用类型,就是复杂类型,在使用过程中会遇到 浅拷贝和深拷贝的问题。
Typeof
typeof 对于基本类型来说,除了 null 都可以显示正确的类型 null的类型显示为object1
2
3
4
5
6
7typeof 1 //'number'
typeof '1' //'string'
typeof undefined //'undefined'
typeof true //'boolean'
typeof Symbol() //'symbol'
typeof b //b 没有声明,但是还是会显示 undefined
typeof null //'object'
typeof 对于对象,除了函数都会显示 object1
2
3typeof [] //'object'
typeof {} //'object'
typeof function(){} //'function'
如果我们想要准确的获得一个变量的正确类型,可以通过 Object.prototype.toString.call(xx)。这样我们就能获得类似 [Object Type]的字符串。
类型转换
转Boolean
除了 undefined, null, 0, false, ‘’, NaN, -0这七种,其他所有的值都为true,包括所有对象。
对象转基本类型
对象在转换基本类型时,首先会调用 valueOf 然后调用 toString。 并且这两个方法也是可以重写的。
四则运算符
如果是加法运算,其中一方是字符串类型,就会把另一个值也转为字符串类型。 其他运算只要其中一方是数字,那么另外一方就会被转为数字。
- [1, 2]+[2, 1] //‘1,22,1’
- ‘a’+ +’b’ //‘aNaN’
原型
每个函数都有 prototype 属性,除了 Function.prototype.bind(),该属性指向原型。
原型是什么?
在javascript中原型是一个prototype对象,用于表示类型之间的关系。
原型链是什么?
javascript中万物都是对象, 对象和对象之间也有关系,并不是孤立存在的。对象之间的继承关系,在javascript中是可以通过prototype对象指向父类对象,直到指向Object对象为止,这样就形成了一个原型指向的链条,叫做原型链。
每个对象都有proto属性,指向创建该对象的构造函数的原型。 对象可以通过proto 来寻找不属于该对象的属性, proto将对象连接起来组成了原型链。
new
- 新生成了一个对象。
- 链接到原型。
- 绑定了this。
- 返回新对象
在调用 new 的过程中会发生以上四件事,我们试着自己来实现一个 new1
2
3
4
5
6
7
8
9
10
11
12function create() {
// 创建一个空的对象
let obj = new Object()
// 获得构造函数
let Con = [].shift.call(arguments)
//链接到原型
obj.__proto__ = Con.prototype
//绑定 this 执行构造函数
let result = Con.apply(obj, arguments)
//确保 new 出来的是个对象
return typeof result === 'object'?result:obj
}
对于实例对象来说,都是通过new 产生的,无论是 function Foo() 还是 let a = { b: 1 }。
instanceof
instanceof 可以正确的判断对象的类型, 其内部机制是通过对象的原型链中是不是能找到类型的prototype。
this
this永远指向最后调用他的那个对象。
new的优先级最高
其次是call, apply, bind 改变 this
箭头函数的this是取决于上级不是箭头函数的函数的this
执行上下文(作用域)
当执行 JS 代码时,会产生三种执行上下文
- 全局执行上下文
- 函数执行上下文
- eval执行上下文
变量提升: 函数 优先级 比变量 高。在提升的过程中,相同的函数会覆盖上一个函数。
let不能在声明前使用,会报错
什么是闭包?
函数A 返回一个函数B,并且函数B中使用了函数A的变量,函数B就是闭包。
深浅拷贝
1 | let a = { |
如果把一个变量赋值给一个对象,那么两者的值会是同一个引用,其中一方改变,另一方也会相应的改变。
通常我们不希望出现这样的问题,我们可以使用浅拷贝来解决这个问题。
浅拷贝
可以用Object.assign来解决这个问题1
2
3
4
5
6let a = {
age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) //1
还可以通过展开运算符…来解决1
2
3
4
5
6
7
8let a = {
age: 1
}
let b = {
...a
}
a.age = 2
console.log(b.age) //1
但是当我们遇到如下情况就需要使用到深拷贝了1
2
3
4
5
6
7
8
9let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = {...a}
a.jobs.first = 'native'
console.log(b.jobs.first) //native
如果接下去的值中还有对象的话,就需要引入深拷贝
JSON.parse(JSON.stringify(object))1
2
3
4
5
6
7
8
9let a = {
age: 1,
jobs: {
first: 'EF'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) //EF
模块化
ES61
2
3
4
5
6
7
8file a.js
export function a() {}
export function b() {}
file b.js
export default function() {}
import {a, b} from './a.js'
import XXX from './b.js
Common
CommonJS 是Node独有的规范1
2
3
4
5
6
7
8a.js
module.exports = {
a: 1
}
b.js
var module = require('./a.js')
module.a //1
AMD
AMD是由RequireJS 提出的1
2
3
4
5
6
7
8
9
10define(['./a', './b'], function(a,b){
a.do()
b.do()
})
define(function(require, exports, module) {
var a = require('./a')
a.doSomething()
var b = require('./b')
b.doSomething()
})
防抖
在一段时间内的多次执行变成最后一次执行
节流
将多次执行变成每隔一段时间执行
继承
ES5模式:1
2
3
4
5
6
7
8
9
10
11
12
13
14function Super() {}
Super.prototype.getNumber = function() {
return 1
}
function Sub() {}
let s = new Sub()
Sub.prototype = Object.create(Super.prototype, {
constructor: {
value: Sub,
enumerable: false,
writable: true,
configurable: true
}
})
以上继承实现思路就是将子类的原型设置为父类的原型
ES6:1
2
3
4
5
6
7class MyDate extends Date {
test () {
return this.getTime()
}
}
let myDate = new MyDate()
myDate.test()
call,apply,bind区别
Promise实现
Promise 是 ES6新增的语法,解决了回调地狱的问题。
可以把Promise看成一个状态机。初始是pending状态,可以通过函数resolve 和 reject, 将状态转变为 resolved或者rejected 状态,状态一旦改变就不能再次变化。
then 函数会返回一个Promise实例,并且该返回值是一个新的实例而不是之前的实例。
Map和Reduce
Map作用是将一个数组遍历。将里面的每个值拿出来做一些变化然后append到一个新数组中返回
Reduce是将数组中的值组合起来,最终得到一个值