JS 总结

数据类型

  1. 基本数据类型:Number、String、Boolean、Null、Undefined、Symbol
  2. 引用数据类型统称为Object,细分有:Object、Array、Function
  3. 存储方式:基本数据类型的数据直接存储在栈中;引用数据类型的数据存储在堆中,在栈中保存数据的引用地址。栈内存是自动分配内存的,而堆内存是动态分配内存的,不会自动释放,所以每次使用完对象的时候都要把它设置为null,从而减少无用内存的消耗。

isNaN和Number.isNaN函数的区别

  1. 函数isNaN接收参数后,会将该参数转换为数值,任何不能被转换为数值的都会返回true,因此非数字值传入也会返回true,会影响NaN的判断。
  2. 函数Number.isNaN会先判断传入参数是否为数字,如果是再继续判断是否为NaN,不会进行数据类型的转换,这种方法对于NaN的判断更为准确。

Object.is()与比较操作符===、==的区别

  1. 使用双等号==,如果两边的类型不一致,会进行强制类型转换后再进行比较
  2. 使用三等号===,如果两边的类型不一致,不会做强制类型转换,直接返回false
  3. 使用Object.is,一般情况下和三等号的判断相同,它处理了一些特殊情况,比如-0和+0不相等,两个NaN是相等的。

判断数据类型

  1. typeof:返回该类型的字符串形式,包括:number、boolean、string、undefined、object、function、symbol。缺点:typeof null的值是object,无法分辨是null还是object。
  2. instanceof:用来判断A是否是B的实例,检测的是原型。缺点:只能用来判断对象是否存在于目标对象的原型链上,不能判断一个对象实例具体属于哪种类型。
  3. constructor:当一个函数F被定义时,JS引擎会为F添加pprototype原型,然后再在prototype上添加一个constructor属性,并让其指向F的引用。当执行var f = new F()时,F原型上的constructor传递到了f上,因此f.constructor == F。
  4. toString:是object的原型方法,调用该方法,默认返回当前对象的[[class]],格式为[object xxx],其中xxx就是对象的类型。对于Object对象,直接调用toString()就能返回[object Object],而对于其他对象,则需要通过call/apply,如:
    Object.prototype.toString().call([]) //[object Array]
    Object.prototype.toString().call(true) //[object Boolean]
    在这里插入图片描述
    在这里插入图片描述

判断数组

  1. Object的toString方法
    Object.prototype.toString.call(obj).slice(8,-1) === ‘Array’
  2. 原型链
    obj._ _ proto_ _ === Array.prototype
  3. isArray
    Array.isArray(obj)
  4. instanceof
    obj instanceof Array

0.1+0.2不等于0.3

  1. 原因:浮点数运算的精度问题。计算机只认识二进制,由于浮点数用二进制表达时是无穷的,IEEE754标准的64位双精度浮点数的小数部分最多支持53位二进制位,所以两者相加后,因浮点数小数位的限制而截断的二进制数字,再转换为十进制,就成了0.30000000000000004
  2. 解决方法
    (1)转为整数运算
function addNumFun(a,b){
	const maxLen = Math.max(
		a.toString().split('.')[1].length,
		b.toString().split('.')[1].length
	)
	const base = Math.pow(10,maxLen)
	return (a*base + b*base) / base
}

null的类型为什么是Object

第一版的JavaScript是用32位比特来存储值的,且是通过值的低1位或低3位来识别类型的。
1:整型(int)
000:引用类型object
010:双精度浮点型double
100:字符串string
110:布尔型boolean
另外还有两个特殊值:undefined用整数-2^30,不在整型的范围内;null机器码空指针,第三位也是000

字面量创建对象和new对象有什么区别?new内部都实现了什么?

  1. 字面量:字面量创建对象更简单,方便阅读;不需要作用域解析,速度更快。
  2. new内部
    (1) 创建一个新对象
    (2)使新对象的__proto__指向原函数的prototype
    (3)改变this指向(指向新的obj)并执行该函数,执行结果保存起来作为result
    (4)判断执行函数的结果不是null或者undefined,如果是则返回之前的新对象,否则返回result
  3. js实现一个new方法
function _new(constructer, ...arg) {
   // 创建一个空的对象
   let resultObj = {};
   // 链接该对象到原型,这样新对象就能访问到原型上面的方法
   resultObj.__proto__ = constructer.prototype;
   // 然后实现步骤3,将新创建的对象作为this的上下文
   let result = constructer.call(resultObj, ...arg);
   // 实现步骤4:如果该函数没有返回对象(即result不是一个对象),则返回this(即resultObj)
   return typeOf result === 'object' ? result : resultobj
}

Object静态方法

静态方法:指部署在Object对象自身的方法。

  1. Object.keys():参数是一个对象,返回一个数组,数组成员是该对象自身的(不包含继承的)所有属性名。只返回可枚举属性。
  2. Object.getOwnPropertyNames():与Object.keys()类似,对于一般的对象来说,这两个返回的结果是一样的,只有涉及不可枚举属性时,才会有不一样的结果。Object.getOwnPropertyNames()还返回不可枚举的属性(比如数组的length)。
  3. 其他静态方法:
    Object.defineProperty():通过描述对象,定义某个属性。
    Object.freeze():冻结一个对象。
    Object.create():该方法可以指定原型对象和属性,返回一个新的对象。
    Object.getPrototypeOf():获取对象的prototype对象。

Object实例方法

  1. Object.prototype.valueOf():返回当前对象对应的值。
  2. Object.prototype.toString():返回当前对象对应的字符串形式。
  3. Object.prototype.hasOwnProperty():判断某个属性是否为当前对象自身的属性,还是继承自原型对象的属性。

闭包

  1. 定义:在一个函数内部有权访问另一个函数内部的变量,本质是在一个函数内部创建另一个函数。
  2. 闭包特性:
    (1)函数嵌套函数
    (2)函数内部可以引用函数外部的参数和变量
    (3)参数和变量不会被垃圾回收机制回收
  3. 好处
    (1)包含函数内的变量安全,实现封装,避免命名冲突
    (2)在内存中维持一个变量,可以做缓存
  4. 坏处
    被引用的变量不能被销毁,增大了内存消耗,造成内存泄漏,解决方法是可以在使用完变量后手动为他赋值为null
  5. 闭包的使用场景
    (1)函数防抖、节流:需要一个变量保存计时,考虑维护全局纯净,可以借助闭包。
    (2)setTimeout:原生的setTimeout传递的第一个函数不能带参数,通过闭包可以实现传参效果。
function f1(a) {
    function f2() {
        console.log(a);
    }
    return f2;
}
var fun = f1(1);
setTimeout(fun,1000);//一秒之后打印出1
  1. 闭包应用
    自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方。
    (1)函数作为参数被传递
function print(fn){
	const a = 200
	fn()
}
const a = 100
function fn(){
	console.log(a)
}
print(fn) // 100

(2)函数作为返回值被返回

function create(){
	const a = 100
	return function(){
		console.log(a)
	}
}
const fn = create()
const a = 200
fn() // 100
  1. 应用实例:比如缓存工具,隐藏数据,只提供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'))  // 100

JS常见内存泄漏及解决方法

  1. 意外的全局变量:使用var、let、const声明,或者js使用严格模式 use strict
  2. 闭包:不要滥用闭包,使用完之后设置为null
  3. 定时器:使用完之后清除,clearInterval或clearTimeout
  4. 事件监听:DOM.addEventListener(“click”, callback),手动解除DOM.removeEventListener(“click”,callback)
  5. 元素引用没有清理:var a = document.getElementById(‘id’);
    document.body.removeChild(a);虽然元素被移除了,但因为存在着变量a对它的引用,DOM元素还在内存里,解决方法:a=null
  6. 控制台日志记录console.log()

原型和原型链

  1. 原型:每个JavaScript对象(null除外)在创建的时候就会与之关联另一个对象,这个对象就是原型。
    (1)prototype:函数的prototype属性指向了一个对象,这个对象正是调用该构造函数而创建的实例的原型,prototype带有__proto__和constructor。
    (2)proto:每个JavaScript对象(null除外)都具有的一个属性,叫__proto__,这个属性指向了创建该对象的构造函数的原型。
  2. 原型链:每个实例对象都有一个__proto__私有属性,指向他的构造函数的原型对象prototype,该原型对象也有一个自己的原型对象__proto__,层层向上直到一个对象的原型对象为null。
    在这里插入图片描述

null和undefined的区别

undefined代表未定义,null代表空对象(主要用于对象初始化)

  1. null表示没有对象,即该处不应该有值,典型用法:
    (1)作为函数的参数,表示该函数的参数不是对象
    (2)作为对象原型链的终点
  2. undefined表示缺少值,此处应该有一个值,但是还没有定义,典型用法:
    (1)变量被声明但没有赋值
    (2)调用函数时,应该提供的参数没提供
    (3)对象没有赋值的属性
    (4)函数没有返回值时默认返回undefined

浅拷贝和深拷贝

  1. 浅拷贝
    (1)for in循环赋值
    (2)解构赋值 var newObj = { …obj }
    (3)var newObj = Object.assign( {},obj )
    第一层的没有改变,一层以下就被改变了
  2. 深拷贝
    (1)递归循环
    function isObject(o){
    	return Object.prototype.toString.call(o) === '[object Object]' || Object.prototype.toString.call(o) === '[object Array]'
    }
    function deepClone(o){
    	if (isObject(o)) {
    		let obj = Array.isArray(o) ? [] : {}
    		for(let item in o){
    			if(isObject(o[item])){
    				obj[item] = deepClone(o[item])
    			}else{
    				obj[item] = o[item]
    			}
    		}
    		return obj
    	} else {
    		return o
    	}
    }
    
    (2)JSON.parse(JSON.stringify)
    (3)lodash的cloneDeep方法
    (4)jQuery的extend方法
    JSON.parse(JSON.stringify())的缺点:
    (1)obj里有date,拷贝后时间会变成字符串的形式,而不是时间对象;
    (2)obj里有RegExp、Error对象,序列化的结果会变成空对象{};
    (3)obj里有function、undefined,会丢失;
    (4)obj里有NaN、Infinity和-Infinity,会变成null;
    (5)JSON.stringify()只能序列化对象的可枚举的自有属性,如果obj中的对象 是由构造函数生成的实例对象,深拷贝后,会丢弃对象的constructor。
    常用的lodash方法:
    (1)深拷贝:cloneDeep()
    (2)对象数组排序:lodash.uniqBy(arr, ‘diagnoseCode’)
    (3)比较两个对象是否相同:_.isEqual(this.initTableForm, this.tableForm)

线程和同步

  1. 单线程
    js是单线程的,即js只能在一个线程上运行
  2. 消息队列(任务队列)
    JavaScript运行时,除了一个运行线程,引擎还提供一个消息队列,里面是各种需要当前程序处理的消息。新的消息进入队列的时候,会自动排在队列的尾端。
    同步任务:在主线程排队支持的任务,前一个任务执行完毕后,执行后一个任务,形成一个执行栈。
    异步任务:异步任务会被主线程挂起,不会进入主线程,而是进入消息队列,而且必须指定回调函数,只有消息队列通知主线程,并且执行栈为空时,该消息对应的任务才会进入执行栈获得执行的机会。
  3. 宏任务(macrotask)和微任务(microtask):异步任务的两个分类。
    宏任务:script、setTimeout、setInterval、setImmediate
    微任务:promise.then、process.nextTick、Object.observe、MutationObserve
    执行过程:
    (1)执行宏任务script
    (2)进入script后,所有的同步任务主线程执行
    (3)所有宏任务放入宏任务执行队列
    (4)所有微任务放入微任务执行队列
    (5)先清空所有的微任务队列
    (6)再取一个宏任务,执行,再清空微任务队列
    (7)依次循环

箭头函数和普通函数的区别

  1. this指向:箭头函数的this指向它定义时所在的对象,普通函数this指向调用时所在的对象
  2. 箭头函数不会进行函数提升
  3. 没有arguments对象,不能使用arguments,如果想要获取参数的话可以使用rest运算符
  4. 没有yield属性,不能作为生成器Generator使用
  5. 不能当作构造函数new,原因:
    (1)没有自己的this,不能使用call和apply
    (2)没有prototype,new关键字内部需要把新对象的__proto__指向函数的prototype

构造函数的arguments:类数组
在这里插入图片描述
箭头函数的rest:

var foo = (...args) => {
	console.log(args)  // [1,2,3]
}
foo(1,2,3)

代理

  1. 定义:在访问对象前添加一层拦截,可以过滤很多操作。
  2. 定义方式:
    (1)字面量定义,对象里面的get和set
    (2)类定义,class里面的get和set
    (3)Proxy对象,传两个参数,第一个是目标对象target,第二个对象是放set和get的handler对象。Proxy和上面两个的区别在于Proxy专门对对象的属性进行get和set
  3. 代理的实际应用:
    (1)Vue的双向绑定:Vue2用的是Object.defineProperty,Vue3用的是Proxy
    (2)校验值
    (3)计算属性值

跨域

  1. JSONP:JSONP利用了script标签可以任意跨域的特点实现的。img的src、link的href、script的src都没有被同源策略限制到。缺点:只支持get请求。
    (1)原生js实现:

    <script>
    	var script = document.createElement('script');
    	script.type = 'text/javascript';
    
    	// 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
    	script.src = 'http://www.domain2.com:8080/loginuser=admin&callback=handleCallback';
    	document.head.appendChild(script);
    
    	// 回调执行函数
    	function handleCallback(res) {
        	alert(JSON.stringify(res));
    	}
    </script>
    

    (2)jQuery Ajax实现

     $.ajax({
     	url:'',
     	type:'GET',
     	dataType:'jsonp',
     	success:function(data){
     		console.log(data)
     	}
     })
    

    (3)Vue axios实现

     this.$http = axios
     this.$http.jsonp(url,{
     	params:{},
     	jsonp:'handleCallback'
     }).then(res => {
     	console.log(res)
     })
    
  2. document.domain + iframe:只有在主域相同的时候才使用它

  3. location.hash + iframe

  4. window.name + iframe

  5. CORS:通过自定义请求头来让服务器和浏览器进行沟通

  6. nginx代理跨域:nginx模拟一个虚拟服务器,因为服务器与服务器之间是不存在跨域的;发送数据时,客户端 -> nginx -> 服务端;返回数据时,服务端 -> nginx -> 客户端

  7. vue 配置proxy代理,原理:在本地运行npm run serve等命令时实际上是用node运行了一个服务器,因此proxy实际上是将请求发给自己的服务器,再由服务器转发给后台服务器,做了一层代理。

回流和重绘

  1. 回流:当节点树中的部分或全部元素因为需要改变尺寸、布局或隐藏而需要重新构建,这个过程叫作回流。回流必将引起重绘。
  2. 重绘:节点树中一部分元素改变,而不影响布局的,只影响外观的,比如颜色,该过程叫作重绘。
  3. 如何减少回流和重绘?
    (1)减少样式的操作:通过class一次性修改样式,而非一个一个改
    (2)避免频繁直接访问计算后的样式,而是先将信息保存下来

事件冒泡和事件捕获有什么区别

DOM2级事件规定事件流包含三个阶段:事件捕获阶段、目标阶段、事件冒泡阶段。
DOM2级事件定义了两个方法:addEventListener()和removeEventListener(),包含三个参数:第一个参数是要处理的事件方式(如click-);第二个是事件处理的函数,可以是匿名函数,也可以是具名函数,但如果需要删除事件,必须是剧名函数;第三个参数是一个布尔值,true表示在捕获阶段调用事件处理程序,false表示在冒泡阶段,默认是false

  1. 事件冒泡:从下至上执行
  2. 事件捕获:从上至下执行

防抖和节流

  1. 防抖:n秒后再执行该事件,如果在n秒内被重复触发,则重新计时。
    function debounce (fn,delay) {
    	let timer
    	return function () {
    		if(timer) clearTimeout(timer)
    		timer = setTimeout(fn,delay)
    	}
    }
    
    防抖应用场景:
    (1)每次resize/scroll触发统计事件
    (2)文本输入的验证(连续输入文字后发送ajax请求进行验证,验证一次就好)
  2. 节流:n秒内只运行一次,如果在n秒内重复触发,只有一次生效。
    function throttle (fn,delay) {
    	let timer
    	return function () {
    		if(!timer){
    			timer = setTimeout(() => {
    				timer = null
    				fn()
    			},delay)
    		}
    	}
    }
    
    节流应用场景:
    (1)DOM元素的拖拽功能实现
    (2)监听滚动事件判断是否到页面底部自动加载更多

call apply bind

  1. 都是用来重定义this这个对象的
  2. bind返回的是一个函数,必须调用它才会执行
  3. 第一个参数都是this的指向对象,call和bind后面的参数以逗号分隔,apply第二个参数是数组
  4. call和apply都是改变上下文中的this并立即执行这个函数,bind方法可以让函数想什么时候调用就什么时候调用,并且可以将参数在调用的时候添加。
    var a = {
        user:"追梦子",
        fn:function(e,d,f){
            console.log(this.user); //追梦子
            console.log(e,d,f); //10 1 2
        }
    }
    var b = a.fn;
    var c = b.bind(a,10);
    c(1,2);
    

for in 和 for of 区别

  1. for in遍历的是数组的索引值(键名);for of遍历的是元素值
  2. for in的缺陷:索引是字符串类型的数字,不能直接进行几何运算;遍历顺序可能不是实际的内部顺序;for in会遍历数组所有的可枚举类型,包括原型通常配合hasOwnProperty()方法来判断某个属性是否是该对象的实例属性,来将原型对象从循环中剔除(arr.hasOwnProperty(key)或Object.prototype.hasOwnProperty.call(arr,key))
    Array.prototype.method=function(){}
    var myArray=[1,2,4];
    myArray.name="数组";
     
    for (var index in myArray) {
        console.log(myArray[index]);    //0,1,2,method,name
    }
     
    for (var value of myArray) {
        console.log(value);    //1,2,4
    }
    
  3. for in更适合遍历对象,for of更适合遍历数组

forEach filter map

  1. forEach:没有返回值;不支持break、return、continue;删除自身元素index不会被重置;for循环可以控制循环起点(i初始化的数字),forEach只能从0开始;for循环可以修改索引,forEach不能。
    forEach不能直接改变数组,直接用item=xxx是不能改变的,但是如果用arr[index]就能改变;如果子元素是对象,可以改变对应属性。
  2. filter:返回一个符合条件的数组,回调函数中有return返回值,返回true时该元素保存到新数组中,不改变原数组。
  3. map:返回一个新数组,数组中的元素为原始数组调用函数处理后的值,不改变原数组。

存储

  1. localStorage:关闭浏览器后数据依然保留,没有过期时间,除非手动清除
    localStorage.setItem('key','item')
    localStorage.getItem('key')
    localStorage.removeItem('key')
    localStorage.clear()   //全部清除
    
  2. sessionStorage:当前会话有效,关闭浏览器或标签后失效
  3. cookie:保存在客户端,可以设置过期时间;存储大小只有4k;在http下cookie是明文传输的,较不安全。
    cookie属性:
    (1)expire:设置过期时间,不设置则与会话期相同
    (2)http-only:不能被客户端更改访问,防止XSS攻击
    (3)Secure:只允许在https下传输
    (4)Max-age:cookie生成后失效的秒数
    document.cookie = "username=jay;expire=Thu, 18 Dec 2043 12:00:00 GMT"
    //删除时,不需要指定cookie的值
    document.cookie = "name=;expire=以前的时间"
    
  4. session
    (1)保存在服务器端
    (2)session的运行依赖sessionId,sessionId保存在cookie中,如果禁用cookie,可以把sessionId放在url中
    (3)session一般用来跟踪用户的状态
    (4)session的安全性更高,一般为使服务端性能更佳,会考虑部分信息保存在cookie中。

从浏览器输入URL都经历了什么

  1. 浏览器先查看浏览器缓存-系统缓存-路由器缓存,如果缓存中有,会直接在屏幕上显示内容,如果没有,跳至第二步操作
  2. 在发送http请求前,先进行域名解析(DNS解析),获取相应的ip地址
  3. 进行http请求,三次握手四次挥手建立断开连接
    为什么需要三次握手?两次不行吗?其实这是由TCP的自身特点可靠传输决定的,客户端和服务端要进行可靠传输,那么就需要确认双方的接收和发送能力,第一次握手可以确认客户端的发送能力,第二次握手确认了服务端的接收能力和发送能力,第三次握手才可以确认客户端的接收能力,不然容易出现丢包的情况。
  4. 服务器处理,将数据返回至浏览器
  5. 浏览器收到响应,从上而下执行代码
    HTML渲染过程
    1. 解析html,创建DOM树
      (1)自上而下解析,遇到任何样式(link、style)和脚本(script)都会阻塞
      (2)css加载不会阻塞html的解析,但会阻塞dom的渲染
      (3)css加载会阻塞后面js语句的执行
      (4)js会阻塞html的解析和渲染
      (5)没有defer和async标签的script会立即加载并执行
      (6)有async标签的js,js的加载执行和html的解析和渲染并行
      (7)有defer标签的js,js的加载和html的解析和渲染并行,但会在html解析完成后执行,在触发DOMContentLoaded事件前执行
      (8)DOMContentLoaded和onload的区别:DOMContentLoaded在html解析完毕后执行,onload在页面完全加载完成后执行(包括样式和图片)
    2. 解析css,生成css对象模型
    3. dom和css合并,构建渲染树
    4. 布局和绘制

window.loation对象所包含的属性

  1. search:搜索内容(从问号开始的URL)
  2. hash:哈希值(从井号#开始的URL)
  3. host:主机名和当前URL的端口号
  4. hostname:主机名
  5. port:端口号

数组常用方法

改变原数组
  1. push:数组末尾添加一个或多个元素,返回新的数组长度
  2. pop:删除并返回数组的最后一个元素,若数组为空,返回undefined
  3. unshift:数组头部添加一个或多个元素,返回新的数组长度
  4. shift:删除并返回数组的第一个元素
  5. sort:对数组元素进行排序
  6. splice(index,num,str1,str2):删除、添加、替换元素,从index位置开始删除num个元素,并将str1、str2从index位置依次插入。返回删除或被替换的值,没有被删除或替换返回空数组。
不改变原数组
  1. concat:合并两个或多个数组
  2. join:按指定字符串分割转换成字符串,默认为逗号。
  3. reverse:将数组倒序。
  4. slice(start,end): 截取从开始位置到结束位置(不包含结束位置),参数可以是负值,-1表示最后一个元素,-2表示倒数第二个元素,以此类推。
  5. forEach:调用数组的每个元素,并将元素传递给回调函数。
  6. filter:过滤数组,符合条件的元素组成一个新数组返回。
  7. every:对数组中的每一项进行判断,都符合条件返回true,否则返回false。
  8. map:原数组的每一项执行函数后,返回一个新的数组。
  9. some:对数组中的每一项进行判断,有一个符合返回true,都不符合返回false。
  10. reduce:接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。第一个参数是初始值或者计算结束后的返回值,第二个参数是当前元素,第三个参数可选,当前元素的索引值,第四个元素可先,当前元素所属的数组对象。
  11. find:返回符合条件的第一个元素的值。
  12. indexOf(item,start):返回从指定位置开始符合条件的元素第一次出现的位置。
  13. findIndex:返回符合条件的第一个元素的索引。
  14. includes:是否包含指定元素,返回true或false。

字符串常用方法(都不改变原字符串)

  1. charAt(index):返回指定下标位置的字符,index在0-str.length-1,否则返回空串。不带参数和参数为NaN返回第一个字符。
  2. charCodeAt(index):返回指定下标的字符的unicode编码,index超出范围返回NaN。
  3. String.fromCharCode(unicode1,unicode2,…,unicodex):返回unicode编码对应的字符串。
  4. indexOf(searchStr,startIndex):返回指定的子字符串在字符串中首次出现的位置,匹配不到返回-1。
  5. lastIndexOf(searchStr,startIndex):返回指定的子字符串在字符串中最后出现的位置。
  6. includes:是否包含指定子字符串,返回true或false。
  7. slice(startIndex,endIndex):返回从开始位置到结束位置的字符串,不包括结束位置。如果为负数,-1表示最后一个字符,-2表示倒数第二个,依次类推,第一个参数要小于第二个参数,否则返回空字符串。
  8. substring(startIndex,endIndex):与slice用法一样,但不接受负数的参数。如果是负数或非法参数,返回原字符串。
  9. substr(startIndex,length):返回从指定位置开始指定长度的子字符串。如果是负数,同slice处理。
  10. split:字符串转换为数组。
  11. search(str/regexp):检索指定字符串或与正则表达式匹配的子字符串,返回匹配的子字符串开始的位置。匹配不到返回-1。
  12. match:返回匹配到的所有字符串组成的数组,无匹配返回null。
  13. replace:替换字符或正则表示式。
  14. toUpperCase、toLowerCase:大小写转换。

js常见的设计模式

  1. 单例模式:不管创建多少个对象都只有一个实例。
    const SingleMode = (function () {
       let instance = null
       function Single (name) {
           this.name = name
       }
       return function (name) {
           if (!instance) {
               instance = new Single(name)
           }
           return instance
       }
    })()
    const oA = new SingleMode('jay')
    const oB = new SingleMode('hll')
    console.log(oA) // {name:'jay'}
    console.log(oB) // {name:'jay'}
    console.log(oA === oB) // true
    
  2. 工厂模式:工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类。该模式使一个类的实例化延迟到了子类。子类可以重写接口方法以便创建的时候指定自己的对象类型。
    function factoryMode (o) {
    	const instance = {}
        instance.name = o.name
        instance.age = o.age
        instance.getInstance = function () {
            return 'name:' + instance.name + ',age:' + instance.age
        }
        return instance
    }
    const obj1 = factoryMode({ name: 'jay', age: '18' })
    const obj2 = factoryMode({ name: 'hll', age: '5' })
    console.log(obj1) // {name:'jay',age:18,getInstance:f()}
    console.log(obj2) // {name:'hll',age:5,getInstance:f()}
    
  3. 发布者-订阅者模式:多个订阅者同时监听同一个数据对象,当这个数据对象发生变化的时候会执行一个发布事件,通知所有的订阅者。
  4. 代理模式:为其他对象提供一种代理以控制对这个对象的访问。
  5. 迭代器模式:提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。
    const each = function (arr, callback) {
        for (let i = 0; i < arr.length; i++) {
            // 将值、索引返回给回调函数处理
            if (callback(i, arr[i]) === false) {
                break// 中止迭代器,跳出循环
            }
        }
    }
    // 外部调用
    each([1, 2, 3, 4, 5], (index, value) => {
        if (value > 3) {
            return false // 返回false中止each
        }
        console.log([index, value]) // [0,1] [1,2] [2,3]
    })
    

js实现继承的几种方式

  1. 原型继承:将父类的实例作为子类的原型
    缺点:来自原型对象的所有属性被所有实例共享;无法向父类构造函数传参。
    function Parent (name) {
        this.name = name
    }
    function Son () {
    
    }
    Son.prototype = new Parent()
    Son.prototype.name = 'jay'
    const son = new Son()
    console.log(son.name) // jay
    console.log(son instanceof Parent) // true
    console.log(son instanceof Son) // true
    
  2. 构造函数继承:在子类的构造函数中,通过 apply ( ) 或 call ( )的形式,调用父类构造函数,以实现继承。缺点:子类实例不能继承父类的构造属性和方法。
  3. 组合继承(结合原型和构造函数继承):通过调用父类,继承父类的属性并保留传参的优点(利用call改变this),再通过将父类实例作为子类原型,使父类原型中的方法也能继承,最后改变子类原型中的constructor。缺点:调用了两次父类的构造函数,造成了不必要的消耗。
    function Parent (name) {
        this.name = name
    }
    function Son (name, age) {
        Parent.call(this, name)
        this.age = age
    }
    Son.prototype = new Parent()
    Son.prototype.constructor = Son
    const son = new Son('jay', 18)
    console.log(son) // {name:'jay',age:18}
    console.log(son instanceof Parent) // true
    console.log(son instanceof Son) // true
    
  4. 寄生组合继承:借用构造函数+相当于浅拷贝父类的原型对象。
    function Parent (name) {
        this.name = name
    }
    function Son (name, age) {
        Parent.call(this, name)
        this.age = age
    }
    Son.prototype = Object.create(Parent.prototype)
    Son.prototype.constructor = Son
    const son = new Son('jay', 18)
    console.log(son) // {name:'jay',age:18}
    console.log(son instanceof Parent) // true
    console.log(son instanceof Son) // true
    
  5. class extends继承:寄生组合继承的语法糖

网络原理

  1. 三次握手和四次挥手
    (1)三次握手:第一次握手:客户端像服务端发送一个SYN包,客户端进入SYN_SENT状态,等待服务端确认;第二次握手:服务端收到SYN后,给客户端返回一个SYN+ACK包,表示已收到SYN,并进入SYN_RECEIVE状态;第三次握手:客户端向服务端发送一个ACK包表示确认,双方进入establish状态,连接已建立。
    为什么是三次握手?
    因为如果只有两次,在服务端收到SYN后,向客户端返回一个ASK就进入establis状态,如果请求中遇到网络问题而没有传给客户端,客户端一直是等待状态,后面服务端发送的信息也接收不了了。
    (2)四次挥手:客户端向服务端发送一个FIN包,进入FIN_WAIT1状态;服务端收到后,向客户端发送ACK包确认,进入CLOSE_WAIT状态,客户端接收到ACK包后进入FIN_WAIT2状态;服务端再把剩余没传完的数据发送给客户端,发送完毕后再发送一个FIN+ACK包,进入LAST_ACK(最后确认)状态;客户端收到后,再向服务器发送ACK包,再等待两个周期后关闭连接。
    为什么等待两个周期?
    因为最后服务端发送的ACK包可能丢失,如果不等待两个周期的话,服务端在没收到ACK包之前,会不停的重复发送FIN包而不关闭。
    为什么是四次握手?
    服务端在收到客户端的FIN报文后,可能还有一些数据要传输,所以不能马上关闭,但是会做出应答,返回ACK报文段,再发送完数据后,再向客户端发送FIN报文,表示数据已经发送完毕,请求关闭连接。
  2. http状态码
    (1)2 开头的表示成功 一般是200
    (2)3 开头的表示重定向
    301永久重定向
    302临时重定向
    304资源未被修改,可以在缓存中取数据
    (3)4 开头表示客户端错误
    403跨域,没权限
    404请求资源不存在
    (4)5 开头表示服务端错误
    500服务器错误
    504网关超时
  3. http1.0、http1.1、http2有什么区别?
    (1)http0.9只能进行get请求
    (2)http1.0增加了post、head、option、put、delete等
    (3)http1.1增加了长连接keep-alive,增加了host域,并且节约宽带
    (4)http2多路复用,头部压缩,服务器推送
  4. http和https有什么区别?https的实现原理?
    (1)http无状态连接,而且是明文传输,不安全
    (2)https传输内容加密,身份验证,保证数据完整性
    在这里插入图片描述

正则表达式

  1. 语法:/正则表达式主体/修饰符
  2. test方法检测一个字符串是否匹配某个模式,返回true或false
  3. exec方法检索字符串中的正则表达式的匹配,返回数组,其中存放匹配的结果,匹配不到返回null
  4. 元字符
    \d 查找数字
    \s 查找空白字符
    \b 匹配单词边界
    \uxxxx 查找以十六进制数规定的unicode字符
  5. 方括号
    [abc] 查找方括号之间的任意字符
    [0-9] 查找任何从0到9的数字
    (x|y) 查找任何以|分隔的字符
  6. 量词
    n+ 至少含有一个
    n* 0个多多个
    n? 0个或1个

js末尾必须加分号的情况

如果下一行的第一个字元是下面5个字符之一,JavaScript将不对上一行句尾添加分号:“(”、“[”、“/”、“+”、“-”

js获取地址栏参数

const str = 'http://baidu.com?name=jay&age=18&sex=0'
const arr = str.slice(str.indexOf('?') + 1).split('&')
const obj = {}
arr.forEach(item => {
    const tempArr = item.split('=')
    obj[tempArr[0]] = tempArr[1]
})
console.log(obj)

关于跨域请求

在这里插入图片描述

http和webSocket

http无法让服务端主动去发送信息,为了解决这个问题,我们有三种常见的解决方案:短轮询、长轮询、webSocket

  1. 短轮询:就是不断的间隔一段时间发一个http请求,直到拿到需要的信息才结束。
  2. 长轮询:向客户端发送一个http请求,服务端长时间将连接挂起,长时间不返回数据,直到拿到需要的信息才返回。
  3. webSocket:和http一样都是基于TCP的应用层协议,可以让服务器将数据主动推送给客户端。默认端口是80和443;协议标识符是ws,如果加密是wss;没有同源限制,客户端可以与任意服务器通信。
    webSocket用法:
// 创建一个webSocket对象
var ws = new webSocket('wss://webchat-bj-test5.clink.cn')
// 实例化对象的onopen属性,用于指定连接成功后的回调函数
ws.onopen = function(evt){
	ws.send('hello')
}
// 实例化对象的onmessage属性,用于指定收到服务器数据后的回调函数
ws.onmessage = function(evt){
	console.log(evt.data)
	ws.close()
}
// 实例化对象的onclose属性,用于指定连接关闭后的回调函数
ws.onclose = function(evt){
	
}
  1. 比较:短轮询实现简单,但浪费带宽和服务器资源;
    长轮询可以减少请求数量和带宽,但要考虑意外关闭的情况,实现复杂。

给一个ul下面插入100个li应该怎么插入?如何优化dom操作?

var ul = document.getElementById('ul')
for(var i=0;i<100;i++){
	var li = document.createElement('li')
	li.innerHTML = 'index:' + i
	ul.appendChild(li)
}

优化dom操作:使用DocumentFragment,它是没有父节点的最小的文档对象,用于存储HTML和XML片段。

var ul = document.getElementById('ul')
var fragment = document.createDocumentFragment()
for(var i=0;i<100;i++){
	var li = document.createElement('li')
	li.innerHTML = 'index:' + i
	fragment .appendChild(li)
}
ul.appendChild(fragment)
  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值