js基础知识笔记

一、变量类型和变量计算

值类型和引用类型

常见值类型

1
2
3
4
5
let a // undefined
const str = 'abc' //string
const n = 100 // number
const b = true //boolean
const s = Symbol('s') //symbol

常见引用类型

1
2
3
4
const obj = {x:100}
const arr = ['a', 'b', 'c']
const n = null // 特殊引用类型,指针指向为空地址
function fn(){} // 特殊引用类型,不用于存储数据,所以没有'拷贝,复制'函数这一说

值类型和引用类型的区别

值类型的数据存在栈中,引用类型值只存地址,数据存在堆中

typeof和深拷贝

typeof能判断哪些类型

识别所有值类型 undefined string number boolean symbol

1
2
3
4
5
let a                       typeof a    // undefined
const str = 'abc' typeof str //string
const n = 100 typeof n // number
const b = true typeof b //boolean
const s = Symbol('s') typeof s //symbol

能判断函数

1
function fn(){}             typeof arr  // function

能识别引用类型(不能再细分)

1
2
3
const obj = {x:100}         typeof obj  // object
const arr = ['a', 'b', 'c'] typeof arr // object
const n = null typeof n // object

深拷贝

  1. 判断是值类型还是引用类型
  2. 判断是数组还是对象
  3. 递归

本地安装一个服务

1
2
npm install http-server -g
http-server -p 8001

深拷贝代码

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
let obj1 = {
name: 'bob',
age: 20,
address: {
city: 'beijing',
home: 'changping'
},
tech: ['c', 'js', 'html', 'css', 'vue']
}

let obj2 = deepClone(obj1)
obj2.address.city = 'shanghai'
console.log(obj1.address.city)

function deepClone(obj){
if( typeof(obj) !== 'object' || obj == null ){
return obj
// obj 是null,或者不是对象或数组,直接返回
}

// 初始化返回结果
let result
if(obj instanceof Array){
result = []
}else{
result = {}
}

如果obj是对象,key为键,是数组,key为下标
for(let key in obj){
if(obj.hasOwnProperty(key)){
// 保证key不是原型的属性
// 递归调用
result[key] = deepClone(obj[key])
}
}

// 返回结果
return result
}

变量计算-类型转换

字符串拼接

1
2
3
let a = 100 + 10    // 110
let b = '100' + 10 // '10010'
let c = true + '10' // 'true10'

==

1
2
3
4
5
100 == '100'        // true
0 == '' // true
0 == false // true
false == '' // true
null == undefined // true

何时使用 === 何时使用 ==
只有一种情况使用 ==

1
2
3
4
<!-- 除了 == null 之外,其他一律使用 === -->
const obj = {x:100}
if(obj.a == null){}
<!-- 相当于 if(obj.a === null || obj.a === undefined) -->

if语句和逻辑运算

truly变量: !!a === true 的变量

falsely变量: !!a === false 的变量

以下为falsely变量

1
2
3
4
5
!!0
!!NaN
!!''
!!undefined
!!false

if语句实际上是判断truely变量和falsely变量

逻辑判断

1
2
3
console.log(0 && 10)        // 0
console.log('' || 'abc') // 'abc'
console.log(!window.abc) // true

二、原型和原型链

题目

  1. 如何判断一个变量是不是数组
  2. 手写一个jquery,考虑插件和扩展性
  3. class的原型本质,怎么理解

知识点

  1. class和继承
  2. 类型判断 instanceof
  3. 原型和原型链

class和继承

class

  1. constructor
  2. 属性
  3. 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 类
class Student{
constructor(name, number){
this.name = name
this.number = number
}
sayHi(){
console.log(
`我叫 ${this.name} , 我的学号是 ${this.number}`
)
}
}
// 通过类 new 对象/实例

const xialuo = new Student('夏洛', 100)
console.log(xialuo.name, xialuo.number)
xialuo.sayHi()

继承

  1. extends
  2. super
  3. 扩展或重写方法
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
// 父类
class People{
constructor(name){
this.name = name
}
eat(){
console.log(`${this.name} eat something`)
}
}
// 子类
class Student extends People{
constructor(name, number){
super(name)
this.number = number
}
sayHi(){
console.log(
`我叫 ${this.name} , 我的学号是 ${this.number}`
)
}
}

// 实例
const xialuo = new Student('夏洛', 100)
console.log(xialuo.name, xialuo.number)
xialuo.sayHi()
xialuo.eat()

// 子类
class Teacher extends People{
constructor(name, major){
super(name)
this.major = major
}
teach(){
console.log(`${this.name} 教授 ${this.major}`)
}
}

// 实例
let wanglaoshi = new Teacher('王老师', '语文')
console.log(wanglaoshi.name, wanglaoshi.major)
wanglaoshi.eat()
wanglaoshi.teach()

原型

类型判断-instanceof

1
2
3
4
5
6
7
xialuo instanceof Student   // true
xialuo instanceof People // true
xialuo instanceof Object // true

[] instanceof Array // true
[] instanceof Object // true
{} instanceof Object // true

class 实际上是 function ,可见是语法糖

1
2
3
4
5
6
7
8
typeof(Student)     //"function"
typeof(People) //"function"

// 隐式原型
console.log(xialuo.__proto__)
// 显式原型
console.log(Student.prototype)
console.log(xialuo.__proto__ === Student.prototype)

原型关系

  1. 每个class都有显式原型prototype
  2. 每个实例都有隐式原型proto
  3. 实例的proto指向class的prototype

基于原型的执行规则

获取xialuo.name或执行xialuo.sayHi()时

  1. 先在自身属性和方法寻找
  2. 找不到就自动去proto寻找

原型链

1
2
3
4
5
6
console.log(Student.prototype.__proto__)
console.log(People.prototype)
console.log(Student.prototype.__proto__ === People.prototype)

xialuo.hasOwnProperty('name') //true
xialuo.hasOwnProperty('sayHi') //false

通过显示原型向上找隐式原型,一层一层向上找,最后object的proto是null

instanceof

通过隐式原型向上找,能不能找到显示原型,能找到,就返回true

手写一个jquery,考虑插件和扩展性

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
45
class jQuery {
constructor(selector) {
const result = document.querySelectorAll(selector)
const length = result.length
for (let i = 0; i < length; i++) {
this[i] = result[i]
}
this.length = length
this.selector = selector
}
get(index) {
return this[index]
}
each(fn) {
for (let i = 0; i < this.length; i++) {
const elem = this[i]
fn(elem)
}
}
on(type, fn) {
return this.each(elem => {
elem.addEventListener(type, fn, false)
})
}
// 扩展很多 DOM API
}

// 插件
jQuery.prototype.dialog = function (info) {
alert(info)
}

// “造轮子”
class myJQuery extends jQuery {
constructor(selector) {
super(selector)
}
// 扩展自己的方法
addClass(className) {

}
style(data) {

}
}

三、作用域和闭包

题目

  1. this在不同的应用场景下,如何取值
  2. 手写bind函数
  3. 实际开发中闭包的应用场景

知识点

  1. 作用域和自由变量
  2. 闭包
  3. this

作用域

  1. 全局作用域
  2. 函数作用域
  3. 块级作用域

自由变量

  1. 一个变量在当前作用域没有定义,但被使用了
  2. 向上级作用域,一层一层寻找,直到找到为止
  3. 如果到全局作用域都没找到,报错 xx is not defined

闭包

作用域应用的特殊情况

  1. 函数作为参数被传递
  2. 函数作为返回值被返回
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 函数作为返回值
function create(){
let a = 100
return function (){
console.log(a)
}
}
const fn = create()
const a = 200
fn()
// 100

// 函数作为参数被传递
function print(fn){
const a = 200
fn()
}
const a = 100
function fn(){
console.log(a)
}
print(fn)

所有自由变量的查找,是在函数定义的地方向上查找,不是在执行的地方向上去找

this

  1. 作为普通函数
  2. 使用call apply bind
  3. 作为对象方法被调用
  4. 在class方法中调用
  5. 箭头函数

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
45
function fn1(){
console.log(this)
}
fn1() // window

fn1.call({x:100}) // {x:100}

const fn2 = fn1.bind({x:200}) //bind返回新的函数
fn2() // {x:200}

const zhangsan = {
name: '张三',
sayHi(){
// this即当前对象
console.log(this)
},
wait(){
setTimeout(function(){
// this == window
console.log(this)
})
}
}

const zhangsan = {
name: '张三',
sayHi(){
// this 即当前对象
console.log(this)
},
wait(){
setTimeout(()=>{
// this 即当前对象
// 箭头函数取上级作用域的值
console.log(this)
})
}
}

class People{
constructor(name){
this.name = name
}
}
const zhangsan = new People('张三')

手写bind函数

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
function fn1(a,b,c){
console.log('this',this)
console.log(a,b)
return 'this is fn1'
}

const fn2 = fn1.bind({x:100},10,20,30)
const res = fn2()
console.log(res)

// 模拟bind
Function.prototype.bind1 = function(){
// 将参数拆解为数组
const args = Array.prototype.slice.call(arguments)

// 获取this (数组第一项)
const t = args.shift()

// fn1.bind(...)中的 fn1
const self = this

// 返回一个函数
return function(){
return self.apply(t, args)
}
}

// 练习: 模拟applay,call

闭包的应用

  1. 隐藏数据
  2. 做一个简单的cache工具
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 闭包隐藏数据,只提供API
function createCache(){
const data = {} // 闭包中的数据,被隐藏,不被外界访问
return {
set: function(key, val){
data[key] = val
},
get: function(key){
return data[key]
}
}
}

const c = createCache()
c.set('a',100)
console.log(c.get('a'))

从0到9,点击每个元素时,弹出对用的数字

1
2
3
4
5
6
7
8
9
10
// 如果i用var定义,是全局,每次打印都是10
for(let i = 0;i < 10; i++){
let a = document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click', function(e){
e.preventDefault()
alert(i)
})
document.body.appendChild(a)
}

四、同步和异步

题目

  1. 同步和异步的区别是什么?
  2. 手写用promise加载一张图片
  3. 前端使用异步的场景有哪些?
1
2
3
4
5
6
7
8
9
10
11
console.log(1)
setTimeout(function(){
console.log(2)
},1000)
console.log(3)
setTimeout(function(){
console.log(4)
},0)
console.log(5)

// 1,3,5,4,2

知识点

单线程和异步

js是单线程语言,只能同时做一件事
浏览器和nodejs已支持js启动进程,如web worker
js和dom渲染公用同一个线程,因为js可修改dom结构
遇到等待(网络请求,定时任务)不能卡住,所以需要异步
基于callback函数形式

1
2
3
4
5
6
7
8
9
10
11
// 异步(callback 回调函数)
console.log(100)
setTimeout(function(){
console.log(200)
})
console.log(300)

// 同步
console.log(100)
alert(200)
console.log(300)

异步不会阻塞代码执行,同步会阻塞代码执行

应用场景

网络请求,如ajax图片加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ajax
console.log('start')
$.get('./data.json', function(data){
console.log(data)
})
console.log('end')

// 图片加载
console.log('start')
let img = document.createElement('img')
img.onload = function(){
console.log('loaded')
}
img.src = 'xxx.png'
comsole.log('end')

定时任务,如setTimeout

callback hell 和 promise

callback hell

1
2
3
4
5
6
7
8
9
10
11
12
13
// 获取第一份数据
$.get(url1, (data1)=>{
console.log(data1)
// 获取第二份数据
$.get(url2, (data2)=>{
console.log(data2)
// 获取第三份数据
$.get(url3, (data3)=>{
console.log(data3)
...
}
})
})

promise

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
function getData(){
return new Promise((resolve,reject)=>{
$.ajax({
url,
success(data){
resolve(data)
},
error(err){
reject(err)
}
})
})
}

const url1 = 'data1.json'
const url2 = 'data2.json'
const url3 = 'data3.json'
getData(url1).then(data1 => {
console.log(data1)
return getData(url2)
}).then(data2 => {
console.log(data2)
return getData(url3)
}).then(data3 => {
console.log(data3)
}).catch(err => console.error(err)

手写promise加载图片

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
const url = 'xxx.png'

function loadImg(src){
const p = new Promise((resolve,reject)=>{
const img = document.createElement('img')
img.onload = () => {
resolve(img)
}
img.onerror = () => {
reject(new Erroe('图片加载失败')))
}
img.src = src
})
return p
}

loadImg(url).then(img => {
console.log(img.width)
}).then(img => {
console.log(img.height)
}).catch(ex => {
console.log(ex)
})

const url2 = 'xxx2.png'
loadImg(url1).then(img1 => {
console.log(img1.width)
return img1 // 普通对象
}).then(img1 => {
console.log(img1.height)
return loadImg(url2) // promise实例
}).then(img2 => {
console.log(img2.width)
return img2 // 普通对象
}).then(img2 => {
console.log(img2.height)
}).catch(ex => {
console.log(ex)
})