JavaScript
原型
我们创建的每一个函数,都可以有一个prototype属性,该属性指向一个对象。这个对象,就是原型。
当我们在创建对象时,可以根据自己的需求,选择性的将一些属性和方法通过prototype属性,挂载在原型对象上。而每一个new出来的实例,都有一个proto属性,该属性指向构造函数的原型对象,通过这个属性,让实例对象也能够访问原型对象上的方法。因此,当所有的实例都能够通过proto访问到原型对象时,原型对象的方法与属性就变成了共有方法与属性。
通过图示我们可以看出,构造函数的prototype与所有实例对象的proto都指向原型对象。而原型对象的constructor指向构造函数。
原型链
我们知道所有的函数都有一个叫做toString的方法。那么这个方法到底是在哪里的呢?
先随意声明一个函数:
其中foo是Function对象的实例。而Function的原型对象同时又是Object的实例。这样就构成了一条原型链。原型链的访问,其实跟作用域链有很大的相似之处,他们都是一次单向的查找过程。因此实例对象能够通过原型链,访问到处于原型链上对象的所有属性与方法。这也是foo最终能够访问到处于Object原型对象上的toString方法的原因。
作用域链
作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的。
闭包
第一种理解(红宝书):是指有权访问另一个函数作用域中的变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量
第二种理解(你不知道的javascript):当函数可以记住并访问所在的词法作用域时,就产生了闭包,这个函数持有对该词法作用域的引用,这个引用就叫做闭包
闭包本质还是函数,只不过这个函数绑定了上下文环境(函数内部引用的所有变量)
缺点:常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
作用(使用场景):可以用来管理私有变量和私有方法,将对变量(状态)的变化封装在安全的环境中,使得这些变量不能被外部随意修改,同时又可以通过指定的函数接口来操作。
闭包有三个特性:1.函数嵌套函数2.函数内部可以引用外部的参数和变量3.参数和变量不会被垃圾回收机制回收
题外话:
JavaScript的作用域就是词法作用域而不是动态作用域,
词法作用域最重要的特征是它的定义过程发生在代码的书写阶段
动态作用域的作用域链是基于调用栈的 词法作用域的作用域链是基于代码中的作用域嵌套
this
JavaScript的this总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。
this的指向:
1、作为普通函数调用(this指向全局对象window对象)
2、作为对象的方法调用(this指向该对象)
3、构造器调用(this指向用new返回的这个对象)
4、call、apply、bind的调用(this指向第一个参数对象)
5、自执行函数中的this永远是window;
6、回调中的this一般都是window;
7、箭头函数中的this,指向父级所在的this;
高阶函数
1、函数作为参数传递
2、函数作为返回值输出
new操作符具体干了什么呢?
1、创建一个新对象
2、将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
3、执行构造函数中的代码(为这个新对象添加属性)
4、返回新对象
继承
1、简单原型链继承
缺点:修改sub1.name后sub2.name也变了,因为来自原型对象的引用属性是所有实例共享的。
2、构造函数式继承
缺点:无法实现函数复用,每个子类实例都持有一个新的fun函数,太多了就会影响性能,内存爆炸。。
3、组合式继承
缺点:子类原型上有一份多余的父类实例属性,因为父类构造函数被调用了两次,生成了两份,而子类实例上的那一份屏蔽了子类原型上父类的。。。又是内存浪费。
4、寄生组合式继承
5、es6的class继承方式
es6引入了class、extends、super、static(部分为ES2016标准)
null和undefined的区别?
1、 定义变量的时候:null现在没有,以后会有,undefined现在没有,以后也不会有
2、 null转为数值是0,undefined转为数值是NaN;
3、报错时:undefined表示”缺少值”,就是此处应该有一个值,但是还没有定义。null表示”没有对象”,即该处不应该有值。
call、apply、bind的区别
三者都是用来改变函数的this对象的指向的。
三者第一个参数都是this要指向的对象,也就是想指定的上下文。
call 传入的参数数量不固定,第二部分参数要一个一个传,用,隔开。
apply 接受两个参数,第二个参数为一个带下标的集合,可以为数组,也可以为类数组。
bind 是返回一个改变了上下文的函数副本,便于稍后调用;apply 、call 则是立即调用 。
本地存储
sessionStorage用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面才能访问并且当会话结束后数据也随之销毁。因此sessionStorage不是一种持久化的本地存储,仅仅是会话级别的存储。
localStorage用于持久化的本地存储,除非主动删除数据,否则数据是永远不会过期的。
cookie 和session
1、cookie数据存放在客户的浏览器上,session数据放在服务器上。
2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,考虑到安全应当使用session。
3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用COOKIE。
4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。每次请求一个新的页面的时候Cookie都会被发送过去,与服务器进行交互。如果向保存大量的数据,建议用session;
XML和JSON的区别?
1、数据体积方面。JSON相对于XML来讲,数据的体积小,传递的速度更快些。
2、数据交互方面。JSON与JavaScript的交互更加方便,更容易解析处理,更好的数据交互。
3、数据描述方面。JSON对数据的描述性比XML较差。
4、传输速度方面。JSON的速度要远远快于XML。
如何实现浏览器内多个标签页之间的通信?
调用localstorge、cookies等本地存储方式
线程与进程的区别
1、一个程序至少有一个进程,一个进程至少有一个线程.
2、线程的划分尺度小于进程,使得多线程程序的并发性高。
3、进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
4、线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
5、从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
渐进增强和优雅降级
渐进增强 :针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。
优雅降级 :一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。
性能优化
-
网页内容
-
减少 http请求次数
-
减少 DNS查询次数
-
避免页面跳转
-
缓存 Ajax
-
延迟加载
-
提前加载
-
减少 DOM元素数量
-
避免 404
-
-
服务器
-
使用CDN(内容分发网络)
-
添加Expires或Cache-Control报文头
-
Gzip压缩传输文件
-
-
CSS
-
将样式表置顶
-
用代替@import
-
-
JavaScript
-
把脚本置于页面底部
-
使用外部JavaScript和CSS
-
精简JavaScript和CSS
-
去除重复脚本
-
减少DOM访问
-
图片
-
优化图像
-
优化CSS Spirite
-
不要在HTML中缩放图片
-
favicon.ico要小而且可缓存
-
如何解决跨域问题?
jsonp、CORS、document.domain+iframe、window.name、window.postMessage
jsonp的原理是动态插入script标签
请解释一下 JavaScript 的同源策略。
这里的同源策略指的是:协议,域名,端口相同,同源策略是一种安全协议。
指一段脚本只能读取来自同一来源的窗口和文档的属性。
哪些操作会造成内存泄漏?
1、内存泄漏指任何对象在您不再拥有或需要它之后仍然存在。
2、垃圾回收器定期扫描对象,并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为 0(没有其他对象引用过该对象),那么该对象的内存即可回收。
3、setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。闭包、控制台日志、循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)
Javascript垃圾回收方法
1、标记清除:这是JavaScript最常见的垃圾回收方式,当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”。
2、引用计数:引用计数的策略是跟踪记录每个值被使用的次数,当声明了一个 变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1,如果该变量的值变成了另外一个,则这个值得引用次数减1,当这个值的引用次数变为0的时 候,说明没有变量在使用,这个值没法被访问了,因此可以将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为0的值占用的空间。
事件、IE与火狐的事件机制有什么区别? 如何阻止冒泡?
事件处理机制:IE是事件冒泡、firefox同时支持两种事件模型,也就是:捕获型事件和冒泡型事件。
阻止冒泡:e.stopPropagation?e.stopPropagation():e.cancelBubble=true;
说说严格模式的限制
变量必须声明后再使用
函数的参数不能有同名属性,否则报错
禁止this指向全局对象
不能使用with语句
增加了保留字
arguments不会自动反映函数参数的变化
设立”严格模式”的目的:
消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
消除代码运行的一些不安全之处,保证代码运行的安全;
提高编译器效率,增加运行速度;
为未来新版本的Javascript做好铺垫。
请解释什么是事件代理
事件代理(Event Delegation),又称之为事件委托。即是把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务。
事件代理的原理是DOM元素的事件冒泡。使用事件代理的好处是可以提高性能。
Event Loop、消息队列、事件轮询
异步函数在执行结束后,会在事件队列中添加一个事件(回调函数)(遵循先进先出原则),主线程中的代码执行完毕后(即一次循环结束),下一次循环开始就在事件队列中“读取”事件,然后调用它所对应的回调函数。这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)
主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码(同步任务)调用各种外部API,它们在”任务队列”中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取”任务队列”,依次执行那些事件所对应的回调函数。
执行栈中的代码(同步任务),总是在读取”任务队列”(异步任务)之前执行。
缓存
浏览器缓存(Browser Caching)是浏览器端保存数据用于快速读取或避免重复资源请求的优化机制,有效的缓存使用可以避免重复的网络请求和浏览器快速地读取本地数据,
http缓存
http缓存是基于HTTP协议的浏览器文件级缓存机制。即针对文件的重复请求情况下,浏览器可以根据协议头判断从服务器端请求文件还是从本地读取文件判断expires,如果未过期,直接读取http缓存文件
indexDB
是一个在客户端存储可观数量的结构化数据,并且为这些数据添加索引进行高性能检索。
cookie
指一般网站为了辨别用户身份、储存在用户本地终端上的数据(通常经过加密)。cookie一般通过http请求中在头部一起发送到服务器端。一条cookie记录主要由键、值、域、过期时间、大小组成,一般用户保存用户的认证信息。
localstorage
localStorage是h5的一种新的本地缓存方案,加快下次页面打开时的渲染速度,除非主动删除数据,否则数据是永远不会过期的。
sessionstorage
也是h5的一种本地缓存方案,数据的存储仅特定于某个会话中,也就是说数据只保持到浏览器关闭,当浏览器关闭后重新打开这个页面时, 之前的存储已经被清除。
网友评论