javascript篇二

一. js中的垃圾回收机制

  1. 定义:防止内存泄漏,不定期的寻找不再使用的变量,释放他们的内存
  2. 回收方式:
    • 标记清除:当一个变量再声明的时候,垃圾回收机制将其标记为‘进入状态’,当它离开执行环境(在函数执行之后),垃圾回收机制将其标记为‘离开状态’。然后根据标记的状态类型来决定是否回收。
    • 引用计数:当声明一个变量并且将一个引用类型赋值给这个变量的时候,这个变量的引用次数加一,当这个变量指向其他引用类型的时候,这个变量的引用次数减一。当引用次数为零的时候,这个变量就会被回收

二. 为什么说函数是第一类对象

  1. 函数可以作为参数传递给其他函数。
  2. 可以作为其他函数的返回值分配到变量当中。
  3. 可以存在于数据结构之中。

三. js数据类型的判断方法

  1. typeof:返回具体的数据类型多用于基本数据类型的判断
  2. A instanceof B (A 一定要是一个 object 类型例如数组和对象类型) 返回布尔值。 其实就是判断 B 的 prototype 是否在 a 的原型链上。
  3. 原型链:(不能用于undefined和null)
    • xxx.constructor 返回一个对应类型的 function,例如:ƒ String(),ƒ Array()
    • xxx.constructor===数据类型 返回布尔值,是的话返回 true,否则返回 false
  4. 通用办法:
    • Object.prototype.toString.call(xxx) 返回’[object 对应的数据类型]’ 例如:‘[object String]’
    • Object.prototype.toString.call(xxx)===‘[object 对应的数据类型]’ 返回布尔值
  5. typeof()和 instanceof 的区别
    • 返回的结果不同,typeof 返回具体的数据类型,instanceof 返回布尔值
    • 结构不同:typeof(xxx) A instanceof B 判断 A 的数据类型是不是 B,前后数据的关联性那个
  6. 注意:
  • typeof arr =>‘object’
  • typeof obj =>‘object’
  • typeof null =>'object'
  • typeof undefined =>‘undefined’
  • typeof NaN =>'number'
  • typeof isNaN =>'function'
typeof 123     // number
typeof '123'   // string
typeof true    // boolean
typeof undefined   // undefined

const arr = []
const obj = {}
typeof {}     // object
typeof []     // object
arr instanceof Array    // true
obj instanceof Object   //true

// 需要注意的几个:
typeof null    // object
typeof NaN    // number
typeof isNaN   // function

// 原型链方法
const str = '123'
const num = 123
str.constructor    // ƒ String() 
num.constructor    // ƒ Number()
arr.constructor    // ƒ Function()
obj.constructor    // ƒ Object()
str.constructor === String    // true
num.constructor === Number   // true
arr.constructor  === Function  // true
obj.constructor === Object   // true

// 通用办法:
Object.prototype.toString.call(str)    // [object String]
Object.prototype.toString.call(num)    //  [object Number]
Object.prototype.toString.call(arr)    //  [object Array]
Object.prototype.toString.call(obj)    // [object Object]
Object.prototype.toString.call(true)   // [object Boolean]
Object.prototype.toString.call(null)   // [object Null]
Object.prototype.toString.call(undefined)  // [object Undefined]
Object.prototype.toString.call(num) === '[object Number]'  // true 
//... 以此类推

四. 判断是否为数组的方法

  1. A instanceof Array
  2. Object.prototype.toString.call(xxx)===‘[object Array]’
  3. Array.isArray(xxx) 返回布尔值
const arr = []
arr.instanceof Array   // true
Object.prototype.toString.call(arr)==='[object Array]'  // true
Array.isArray(arr)    // true

五. for in 和 for of的区别

  1. for…in…:一般用来遍历对象,返回键;如果用来遍历数组和字符串则返回索引。
  2. for…of…:一般用来遍历字符串和数组,返回的是数组和字符串中的每一个值。

六. 字符串和数字之间的转换

  1. 字符串转数字:
    • Number(‘123’): 将一个字符串转换成整型或者浮点型,只能用于十进制,不能出现除了小数点之外的非数字字符否则返回NaN。
    • parseInt(‘123’):将一个字符串转换成整型,只能解析整数部分
    • parseFloat(‘123.45’):将一个字符串转换成整型,可以解析小数。
    • +‘123’:在前面添加“+”,建议用这种方法。
  2. 数字转字符串:
    • String(123)
    • number.toString(radix):radix 在 2-36 之间,默认是 10,代表十进制,还可以将一个数组直接展开成字符串
    • number.toFixed(a):小数点后精确到 a 位,会四舍五入
    • number.toExponential(a):用科学计数法,小数点后精确到 a 位
    • number.toPrecision(a):a 表示指定的有效数字位数
// 1.字符串转数字
Number('123')    // 123
Number('123.45') //123.45
Number('123a')   // NaN

parseInt('123')  // 123
parseInt('123.45') // 123

parseFloat('123.45')  // 123.45

+ '123.45'  // 123.45

// 1. 数字转字符串
const num = 123
const num2 = 123.456
const num3 = 123456.789
String(num)    // '123'
String(num2)    // '123.456'
num.toString()  // '123'
num2.toString()  // '123.456'
num2.toFixed(2)  // '123.46' 小数点后面保留两个两位 会四舍五入
num3.toExponential(3)  // '1.235e+5' 会四舍五入
num2.toPrecision(4)   // '123.5' 会四舍五入

七. 事件委托

  1. 概念:将本来绑定在子元素上面的事件现在绑定到了它的父元素上面,通过在父元素上面触发事件来实现对子元素的监听。
  2. 原理:事件冒泡
  3. 优点:减少事件数量,提高程序性能。
  4. 事件:当用户或者浏览器与页面进行交互的时候,产生可以被 javascript 侦测到的行为称之为事件。

八. 构造函数普通函数

  1. 构造函数:主要用来创建类,通常和new一起使用。
  2. 区别:
    • 构造函数只能由 new 关键字调用
    • 构造函数可以创建实例化对象
    • 构造函数是类的标志

九. 自执行函数

  1. 概念:声明一个匿名函数,可以立即发起调用。
  2. 应用:创建一个独立的作用域。
  3. 优点:
    • 隔离作用域,避免污染
    • 避免由闭包造成引用变量无法释放
    • 利用立即执行的特点,返回需要的业务函数或者对象

十. 函数节流

  1. 概念:当一个函数执行一次之后,只有大于设定的执行周期才会执行下一次,保证在一段的时间之内函数只会被执行一次
  2. 例子:
 function throttle(func,wait){
    let timer=null;
    return function(value){
   	if(!timer){
	    timer=setTimeout(()=>{
		   fun(value)
		   timer=null;
   	    },wait)
  	 }
   }
}
 function callback(value){
	  console.log(value)
 }
 throttle(callback,1000)

十一. 函数防抖

  1. 概念:当一个函数在被频繁调用之后,只执行最后一次或者第一次,其余均不生效。
  2. 例子:
 function debounce(callback,wait){
  let timer=nullreturn function(value){
	  if(timer){
	 	 clearTimeout(timer)
	  }
	  timer=setTimeout(()=>{
	 	 callback(value)
	  },wait)
 	 }
  }
  function callback(value){
  	console.log(value)
  }![请添加图片描述](https://img-blog.csdnimg.cn/e68333ba07d34832b15fe8fb065996c7.png)

  debounce(callback,1000)

ps:关于函数防抖和函数节流后面会单独出一篇详细说明。

十二. 赋值、深拷贝和浅拷贝

  1. 赋值:赋值的是该对象在栈中的地址,而不是堆中的数据。如果原对象的属性值是基础类型,那么就拷贝基础类型,赋值之后两个变量互不影响。如果是引用类型,则拷贝的是指针,赋值之后两个变量相互影响。
  2. 浅拷贝:会开辟一个栈内存。一种引用层面上的拷贝,拷贝的是指针,两者所指向的内存并没有发生变化,改变一个另一个也会随之改变。
  3. 深拷贝:会开辟一个新的空间(堆)用于存储新的对象,通过递归的方式复制所有的属性,是一种完全意义上的拷贝,两者所指向的内存发生了变化,改变一个另一个不会随之改变。
    请添加图片描述
  4. 实现浅拷贝的方法
    • 对象的Object.assign()
      • 一层(深拷贝)
      • 多层(浅拷贝)
    • 数组的concat(),slice()
    • 手写循环
// 1.Object.assign()
// 一层
const obj1 = {
	name: 'wangjiajia',
	age: 18
}
const shallowObj = Object.assign({},obj1)
shallowObj.name = 'wangtongtong'
obj  // {name: 'wangjiajia', age: 18},可以看到name的值并没有改变,是深拷贝。

// 多层
const obj2 = {
	person: {
		name: 'wangjiajia',
		age: 18
	}
}
const shallowObj = Object.assign({},obj2)
shallowObj.person.name = 'wangtongtong'
obj2   // {person: {name: 'wangtongtong',age: 18}} 可以看到name的值改变了,是浅拷贝。

// 2.数组的concat()方法
const arr1 = [1,2,{
	name: 'wangjiajia'
}]
const shallowArr = arr1.concat()
shallowArr[2].name = 'wangtongtong'
arr1[2].name   // 'wangtongtong' 可以看到name的值改变了,是浅拷贝

// 3.数组的slice()方法
const arr1 = [1,2,{
	name: 'wangjiajia'
}]
const shallowArr = arr1.slice()
shallowArr[2].name = 'wangtongtong'
arr1[2].name   // 'wangtongtong' 可以看到name的值改变了,是浅拷贝

// 4.手写循环(一层是深拷贝,多层是浅拷贝)
const obj2 = {
	name: 'wangjiajia',
}
const obj3 = {
	person:{
		name: 'wangjiajia'
	}
}
const shallowCopy = (obj)=>{
	const targetObj = {}
	for(let i in obj){
		targetObj[i] = obj[i]	
	}
	return targetObj 
}

const deepObj = shallowCopy(obj2)
const shallowObj = shallowCopy(obj3)
deepObj.name = 'wangtongtong'
shallowObj.person.name = 'wangtongtong'

obj2.name   // 'wangjiajia' 一层是深拷贝,原来的值不会变化
obj3.person.name   // 'wangtongtong' 多层浅拷贝,原来的值会受到影响,改变了
  1. 实现深拷贝的方法
    • JSON.parse(JSON.stringify())
    • 手写递归方法
      • 原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。
    • 函数库lodash中的cloneDeep方法
// 1. JSON.parse(JSON.stringify())方法
const obj = {
	person: {
		name: 'wangjiajia'
	}
}
const deepObj = JSON.parse(JSON.stringify(obj))
deepObj.person.name = 'wangtongtong'
obj.person.name    // 'wangjiajia'  深拷贝,没有影响到原来的值

// 2.手写递归
const obj1 = {
	name: 'wangjiajia'
}
const obj2 = {
	person:{
		name: 'wangjiajia'
	}
}
// 定义检测数据类型的方法
function checkType(target){
	return Object.prototype.toString.call(target).slice(8,-1)  // 返回数据类型
}
// 实现深拷贝
function deepCopy(target){
	// 判断数据类型,初始化result存放拷贝后的数据
	let result,targetType = checkType(target)
	if(targetType  === 'Object'){
		result = {}
	}else if(targetType === 'Array'){
		result = []
	}else {
		return target
	}
	// 遍历target
	for(let i in target){
		// 判断每一项值得数据类型,如果是Object或Array则继续遍历,否则直接返回
		let value = target[i]
		if(checkType(value) === 'Object' || checkType(value) === 'Array') {
			result[i] = deepCopy(value)
		}else{
			result[i] = value
		}
	}
	return result
}
const deepObj1 = deepCopy(obj1)
const deepObj2 = deepCopy(obj2)
deepObj1.name = 'wangtongtong'
deepObj2.person.name = 'wangtongtong'

obj1.name   // 'wangjiajia'
obj2.person.name    // 'wangjiajia'

// 3. lodash函数库
const _ = require('lodash')
const obj2 = _.cloneDeep(obj1)
obj2 === obj1   // false

十三. 怎么样提高前端性能优化

  1. 尽可能减少http的请求次数,可以将请求的结果存放在一个变量中,便于后续使用。(vue中可能会将结果存进state中)
  2. 减少外部资源的引用,外部资源可以使用 cdn 托管,开启 gzip 压缩文件
  3. css 代码放在头部,js 代码放在尾部
  4. 减少 DOM 操作
  5. 预加载图片,可以将图片提前存放在 sessionStorage 中,然后根据实际需求从中获取
  6. 合并精灵图(一张图片上面有很多样式,通过 css 的办法呈现出需要的图片)
  7. 减少 cookie 头信息(头信息越大,资源传输的越慢)
  8. 对于资源可以按需加载,异步加载

十四. 异步执行的原理

  1. 在 js 中分为同步任务和异步任务,同步任务会被分配到主线程中,异步任务会被分配到任务队列中
  2. 先执行主线程中的同步任务,执行完了之后在执行任务队列里面的异步任务
  3. 通过循环遍历任务队列的方式

十五.宏任务和微任务

  1. 宏任务:script,setTimeout,setInterval,setImmediate
    • 宏任务队列所处的任务
    • 一个宏任务队列里面只能有一个宏任务
    • 可以有多个宏任务队列
  2. 微任务:promise.then,process.nextTick,object.observe
    • 微任务队列所处的任务
    • 一个微任务队列里面可以有多个微任务
  3. 执行顺序
    • 先执行主线程,遇到 new promise 立即执行
    • 执行所有的微任务
    • 执行下一个宏任务
      关于js任务执行顺序看一个例子:
// 例1:
setTimeout(function () {
 	console.log("AAA");
}, 0);

console.log('BBB');

new Promise(function (resolve) {
  console.log("CCC");
  resolve();
 }).then(function () {
  console.log("DDD");
 });

console.log("EEE");
// 执行顺序为:BBB CCC EEE DDD AAA

// 例2:
async function async1() {
  console.log("FFF");
  async2();
  console.log("GGG");
}

async function async2() {
  console.log("HHH");
}

setTimeout(function () {
 	console.log("AAA");
}, 0);

async1();

console.log('BBB');

new Promise(function (resolve) {
  console.log("CCC");
  resolve();
 }).then(function () {
  console.log("DDD");
 });

console.log("EEE");
// 执行顺序为:FFF HHH GGG BBB CCC EEE DDD AAA
// 注意:async函数内部没有await的时候就是一个纯同步函数

// 例3:
async function async1() {
  console.log("FFF");
  await async2();
  console.log("GGG");
}

async function async2() {
  console.log("HHH");
}

setTimeout(function () {
 	console.log("AAA");
}, 0);

async1();

console.log('BBB');

new Promise(function (resolve) {
  console.log("CCC");
  resolve();
 }).then(function () {
  console.log("DDD");
 });

console.log("EEE");
// FFF HHH BBB CCC EEE GGG DDD AAA
// 注意:await async2(); 后面的会放进微任务中

十六. 栈、堆、队列

  1. 队列
    • 一种先进先出的数据结构
    • 一种先进后出的数据结构
    • 自动分配相对固定大小的内存空间,并且由系统自动释放
    • js 的基本数据类型就是由栈存放
    • 程序在运行的时候动态的分配一块大小不一内存空间,不会被系统自动释放
    • js 中的数组,对象,函数都是由堆存放的

十七. new的作用

  1. 作用:开辟一个新的空间来存储构造函数中初始化的数据,并将地址作为返回值返回,构造函数中的 this 指向全局变量,如果没有返回值就会显示 undefined
  2. 实现步骤:
    • new 创建一个新的对象
    • new 会让 this 指向这个新的对象
    • 执行构造函数里面的代码,目的是给新对象加属性和方法
    • 返回这个对象(因此在构造函数中不需要 return)

十八. if 判断类型总结

  1. 定义了但是没有赋值认为是假
  2. 定义了赋值为空字符串认为是假,赋值非空字符串认为是真
  3. 赋值为 true 为真,赋值为 false 为假
  4. 赋值为 0 是假,赋值非零数字是真
  5. 赋值 null、undefined 都是假
  6. 赋值函数
    • 不带括号 if(test){} 如果 test 定义了那就为真,没定义会报错
    • 带括号 if(test()){} 根据 test 函数的返回值从而判定是真还是假

十九. 为什么 0.1+0.2 不等于 0.3

0.1 和 0.2 再转化为二进制之后都是一个近似值,因此相加之后大约等于 0.30000000000000004(15 个 0)不等于 0.3

二十. js里面怎么比较两个对象是否相同

  1. JSON.stringify(obj) 将对象转译成json字符串在进行比较,但是可能会存在问题,用这种方法两个对象的键值顺序必须完全相同。
  2. Object.keys():列举出所有的键,先判断数组长度是否相等,若不相等直接返回false,若相等在判断每一个键对应的值是否相等。
  3. ES6方法:Object.entries(obj1).toString() === Object.entries(obj2).toString(),键值的排列顺序也要相同
  4. lodash 函数库中的 isEqual: isEqual(obj1,obj2) 返回 true or false
const obj1 = {
	name: 'wangjiajia',
	age: 18
}
const obj2 = {
	name: 'wangjiajia',
	age: 18
}
const obj3 = {
	age: 18,
	name: 'wangjiajia',
}
const obj4 = {
	name: 'wangtongtong',
	age: 18
}
// 方法1:JSON.stringify(obj) 
JSON.stringify(obj1) === JSON.stringify(obj2)    // true
JSON.stringify(obj1) === JSON.stringify(obj3)    // false obj1和obj3实际上一样只不过键值顺序不同,但还是返回false。这就是这种方法存在的缺陷。
JSON.stringify(obj1) === JSON.stringify(obj4)    // false

// 方法2:Object.keys()
function compare(obj1,obj2){
	const obj1Keys = Object.keys(obj1)
	const obj2Keys = Object.keys(obj2)
	if(obj1Keys.length !== obj2Keys.length){
		return false
	}else{
		for(let i of obj1Keys){
			if(obj1[i] != obj2[i]){
				return false
			}
		}
		return true
	}
}
compare(obj1,obj2)   // true
compare(obj1,obj3)   // true
compare(obj1,obj4)   // false

// 方法3:es6里面的Object.entries(obj1).toString()
Object.entries(obj1)  // [['name','wangjiajia'],['age',18]] 这种方法会返回有键和值构成的数组,每个数组中都是两个元素,一个是键,一个是值。
Object.entries(obj1).toString()  // 'name,wangjiajia,age,18',将所有的键值组成一个字符串。
Object.entries(obj1).toString() === Object.entries(obj2).toString()  // true
Object.entries(obj1).toString() === Object.entries(obj3).toString()  // false
Object.entries(obj1).toString() === Object.entries(obj4).toString()  // false

// 方法4:lodash函数库里面的isEqual(obj1,obj2)方法
isEqual(obj1,obj2)   // 相同返回true,不相同返回false,和键值的排列顺序无关

请添加图片描述
请添加图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值