# 基础问题
# js数据类型
基本类型: Number、String、Boolean、Undefined、Null
引用类型: Object
- 字符串使用单引号或者双引号或者\ 最好先对它进行转义, ' " \
- 逻辑运算符 &&有假为假 ||有真为真
- 逻辑运算符优先级:! > && > ||
- 逻辑表达式中如果遇到一个非布尔类型的操作数,这个操作数的值就会被返回,而不是true和false。
var b = 5;
true && (b = 6) // 6
true && 'something' // something
- parseInt() 两个参数,第一个解析的数值,第二个以什么进制解析这个数值。
- isNaN() 用来判断某个值是否可以参与数值运算,在对数据进行计算前进行判断很有必要。
# var、let、const区别
var 变量不存在块级作用域 会变量提升
let 变量 不存在变量提升, 存在暂时性死区,只在所在的代码块内有效
const 常量 后面代码中不能修改该常量的值
# Undefined和Null的区别
Null是一个空对象引用,typeof值为object,代表是一个对象,所以最好用于未来可能是对象的值
Undefined表示一个变量没有被声明,或者被声明了但没有被赋值(未初始化)typeof为undefined
# 获取时间戳
Date.now() === (new Date()).getTime()
(new Date()).getDay() // 获取星期
(new Date()).getDate() // 获取日期
# 判断js数据类型
基本类型可以使用typeof
来进行判断
typeof 'ConardLi' // string
typeof 123 // number
typeof true // boolean
typeof Symbol() // symbol
typeof undefined // undefined
你还可以用它来判断函数类型:
typeof function(){} // function
但当是null
和引用类型就不能用typeof判断了
typeof null // object
typeof [] // object
typeof {} // object
typeof new Date() // object
typeof /^\d*$/; // object
使用instanceof
来判断
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。可以用来判断是什么类型。
var obj = {}
var arr = []
obj instanceof Object // true
arr instanceof Object // true
obj instanceof Array // false
arr instanceof Array // true
function(){} instanceof Object // true
可以看出instanceof
并不是一个很好的选择。
使用 toString 方法
Object对象直接使用,因为其他类型重写了toString,所以要通过call/apply来调用。返回一个类型[Object xxx] 的字符串
var obj = {}
var arr = []
obj.toString() // "[object Object]"
Object.prototype.toString.call(arr) // "[object Array]"
Object.prototype.toString.call('') // "[object String]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(1) // "[object Number]"
判断是不是数组
1. Array.isArray(xx) // 判断是不是数组
2. instanceOf操作符
var obj = {}
var arr = []
obj instanceof Object // true
arr instanceof Object // true
obj instanceof Array // false
arr instanceof Array // true
# 什么是作用域
一个包含变量、对象、函数的集合
# for循环
for循环里只有立即执行的表达式才能实时获取每一个i值,通过在外部调用里面的函数或是setTimeOut等延迟执行的都无法获取实时的值,只能获取最后一个值。
# 闭包
简单讲是可以获取其他函数内部作用域的函数
一个函数保留了访问另一个函数作用域的链接
一个函数创建就形成了一个闭包,因为能访问全局作用域。
可以通过return 或者 把函数赋值给全局变量来实现
闭包只保留作用域的访问权,不能访问具体的值。
# new一个对象发生了什么
创建空对象;
var obj = {};
设置新对象的constructor属性为构造函数的名称,设置新对象的__proto__属性指向构造函数的prototype对象;
obj.proto = ClassA.prototype;
使用新对象调用函数,函数中的this被指向新实例对象:
ClassA.call(obj);//{}.构造函数();
如果无返回值或者返回一个非对象值,则将新对象返回;如果返回值是一个新对象的话那么直接直接返回该对象。
# defer和async 异步加载
script
标签添加defer
或者async
属性,脚本就会和后续文档并行加载(异步加载)不会阻塞文档。
defer
与async
的区别是:defer
要等到其他渲染结束(DOM结构完成,以及其他脚本执行完成),才会执行;async
是一旦加载载完成,渲染引擎就会中断其他命令来执行这个脚本。都是并行加载,一个是等其他结束才执行,一个是加载完成就执行。
都是和后面命令同步下载,不阻塞进程,但执行时机不一样。
# ready/onload的区别
ready表示文档结构(DOM结构)已经加载完成(不包含图片等非文字媒体文件),和DOMContentLoaded一样。
onload表示页面包含图片等文件在内的所有元素都加载完成。
所以ready要比onload先执行一些。
# http状态码
http状态码主要分为5种类型:
- 1** 信息反馈,服务器收到请求,需要请求者继续执行操作
- 2** 请求成功,操作被成功接收并处理
- 3** 重定向,需要进一步的操作以完成请求
- 4** 客户端错误,请求包含语法错误或无法完成请求
- 5** 服务器错误,服务器在处理请求的过程中发生了错误
常见的几个状态码:
200 请求成功
301 永久重定向
302 临时重定向
400 客户端请求的语法错误,服务器无法理解
401 未授权,需要身份验证
403 禁止访问 服务器理解请求客户端的请求,但是拒绝执行此请求
404 服务器找不到请求的资源(网页),请求路径不对。
405 请求方法被禁止。
409 服务器请求时发生冲突
500 服务器内部错误,无法完成请求。
501 服务器不支持请求的功能,无法完成请求。
502 (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。
503 (服务不可用) 服务器目前无法使用(由于超载或停机维护)。
# 打印&&、||
// 根据判断条件来打印其中某个值,而不是返回两个共同的结果
// 先判断第一个是否为真,为真取第二个,为假取第一个
console.log(true && true) // true
console.log(true && false) // false
console.log(true && 1) // 1
console.log(true && 0) // 0
console.log(false && false) // false
console.log(false && true) // false
console.log(false && 1) // false
console.log(false && 0) // false
console.log(0 && 1) // 0
console.log(1 && 0) // 0
// 有真取真 没有取最后一个
console.log(true || true) // true
console.log(true || false) // true
console.log(true || 1) // true
console.log(true || 0) // true
console.log(false || true) // true
console.log(false || false) // false
console.log(false || 0) // 0
console.log(false || 1) // 1
console.log(0 || 1) // 1
console.log(1 || 0) // 1
console.log(false || 0 || '') // ''
// 按顺序计算
// 1 && 0 取 0, 0 && 1 取 0
console.log(1 && 0 && 1) // 0
// 0 && 1 取 0,0 && 1 取 0
console.log(0 && 1 && 1) // 0
console.log(1 && false && 1) // false
console.log(1 || false && false) // false
console.log(false || 0 && null) // 0
console.log(typeof false && 1) // 1
console.log(typeof '' && 1) // 1
console.log(typeof null && 1) // 1
console.log(1 && typeof true) // boolean
console.log(1 && typeof null) // object
# MVC和MVVM模式
MVC模式:
- M 模型(Model):数据(js变量)
- V 视图(View):用户界面(HTML,CSS)
- C 控制器(Controller):事件交互、业务逻辑(DOM绑定事件,操作变量)
用户通过View传送指令到Controller,Controller完成业务逻辑,改变Model数据的状态,Model将新的状态反应到View视图上,用户得到反馈。
MVVM模式:
- M 模型(Model):数据、变量
- V 视图(View):用户界面
- VM (ViewModel):实现数据的双向绑定,就是VUE,省去了DOM操作。
# 构造函数不使用new时
对于 sarah,我们没有使用 new 关键字。当使用 new 时,this 引用我们创建的空对象。当未使用 new 时,this 引用的是全局对象(global object)。
this属性指了向全局window,firstName被挂在了window上,而sarah本身依然是undefined。
function Person(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
const sarah = Person('Sarah', 'Smith')
console.log(sarah) // undefined
console.log(window.firstName) // Sarah
# 标签模板功能
ES6标签模板:当模板字符串紧跟在一个函数名后面的时候,该函数将被调用来处理这个模板字符串。这被称为“标签模板”功能(tagged template)。
当函数使用字符串模板时,带有变量的模板字符串会被先处理成参数形式,再传入函数中。规则就是: 字符串中的非变量被拆成数组形式为arr,然后变量作为值紧跟数组后面作为参数...values传入,没有变量就只传数组。
arr:模板字符串中所有那些没有变量替换的部分,没有变量就是一个原字符元素数组,如果有变量,则原字符被变量分割成多个字符数组,如果变量在开始,则数组第一个元素为一个空字符。arr数组的最后一项raw保存的是转义后的原字符串。 …values:各个变量替换后的变量值
let a = 'Oh!';
let b = 'the';
let c = 'very much!'
function tag(arr, ...values) {
console.log(arr);
for(let i in values){
console.log(values[i]);
}
}
tag `${a} I love ${b} JavaScript ${c}`;
//["", " I love ", " javascript ", "", raw: Array[4]]
//oh!
//the
//very much!
tag`jjkskfs ${b}ddfdf`
// ["jjkskfs ", "ddfdf", raw: Array(2)]
tag`jjkskfs dfdf`
// ["jjkskfs dfdf", raw: Array(1)]
# 对象的键值底层都是字符串
const obj = { 1: 'a', 2: 'b', 3: 'c' }
const set = new Set([1, 2, 3, 4, 5])
obj.hasOwnProperty('1') // true
obj.hasOwnProperty(1) // true
set.has('1') // false
set.has(1) // true
# 扩展运算符(...args)会返回实参组成的数组
function getAge(...args) {
console.log(typeof args)
}
getAge(21) // "object"
# break、return和continue之间的区别
- break 会跳出循环并终止循环。
- continue只跳出本次迭代,循环会继续运行、
- return 在函数中返回值,其后面代码不会执行。只在函数中出现,如果不是在函数中会报语法错误 SyntaxError
# try catch捕获异常
在 try 里无论是代码错误,还是使用 throw 抛出错误,都会被 catch 捕获到,并执行 catch 代码块中的语句。try...catch 后还可以跟随一个 finally 代码块,无论是否抛出异常,finally 都会执行。
try {
throw 'error' // 抛出错误
} catch (e) {
console.log(e); // 'error' e就是代码错误或抛出的错误
} finally {
console.log('finally') // 'finally'
}
# delete 关键字
使用delete删除对象的属性,对原型和全局变量都适用,因为全局变量挂载在全局对象上,但是如果通过var let const 声明过就不行了,不在是一个对象了。删除操作返回一个布尔值。
const name = "Lydia";
age = 21;
console.log(delete name); // false
console.log(delete age); // true
# JSON.stringify(value[, replacer])
JSON.stringify的第二个参数是 替代者(replacer). 替代者(replacer)可以是个函数或数组,用以控制哪些值如何被转换为字符串。
如果替代者(replacer)是个 数组 ,那么就只有包含在数组中的属性将会被转化为字符串。
const settings = {
username: "lydiahallie",
level: 19,
health: 90
};
const data = JSON.stringify(settings, ["level", "health"]);
console.log(data);
// "{"level":19, "health":90}"
# import导入模块会先执行
// index.js
console.log('running index.js');
import { sum } from './sum.js';
console.log(sum(1, 2));
// sum.js
console.log('running sum.js');
export const sum = (a, b) => a + b;
执行 running sum.js, running index.js, 3 会先打印sum.js中的,如果使用require则会按顺序打印。
# 箭头函数
1、箭头函数的this指向定义时所在的对象,而不是使用时所在的对象。所以在一个对象里不要使用箭头函数,里面的this不是指向对象,而是对象所在的环境。
2、箭头函数不可当做构造函数,没有原型prototype属性,不能new
# 尾调用优化
在最后一步通过return 执行一个函数,没有其他步骤。如果在函数A中调用函数B,A就会保留B的调用帧,等B结束A才会消失。尾调用是在A最后调用B,A执行自己的调用帧后就结束了,不需要保持自己的内部状态和B的调用帧,所以如果每个函数都是尾调用,这样就能大大节省内存。
只在严格模式下开启。。。
function A() {
return B();
}
# 尾递归
尾递归就是递归函数尾调用自己。递归会保持很多调用帧,而尾递归只需要保存自己一个调用帧就可以,大大节省内存。
正常递归:
function factorial(n) {
if (n === 1) return 1;
return n * factorial(n - 1); // 不是直接返回函数
}
factorial(5) // 120
改写成尾递归:确保最后一步只调用自己
function factorial(n, total) {
if (n === 1) return total;
return factorial(n - 1, n * total); // 直接返回函数
}
factorial(5, 1) // 120
# 函数柯里化
# url从输入到页面生成发生了什么
解析 DNS解析获取IP地址
TCP三次握手连接服务器
客户端发生HTTP请求
服务端发送响应
浏览器解析内容渲染页面
渲染
解析HTML生成DOM树
解析CSS生成CSSOM规则树
将DOM树与CSSOM规则树合并在一起生成渲染树
遍历渲染树开始布局,计算每个节点的位置大小信息
将渲染树每个节点绘制到屏幕
# 重排与重绘
重排:DOM元素的添加或删除、改变位置或大小时,会导致浏览器重新生成渲染树,这个过程叫重排。
重绘:当重新生成渲染树后,就要将渲染树每个节点绘制到屏幕,这个过程叫重绘。
不是所有的动作都会导致重排,例如改变字体颜色,只会导致重绘。记住,重排会导致重绘,重绘不会导致重排 。
减少重排或重绘:
用 JavaScript 修改样式时,最好不要直接写样式,而是替换 class 来改变样式。
# 性能优化
减少http请求(雪碧图和字体图标)
使用CDN
缓存
压缩代码和压缩文件 (JS、CSS、图片)
懒加载和预加载
减少重排和重绘,减少操作DOM
CSS放头部,JS放底部