1、函数声明与函数表达式
(1)变量声明会置顶提前,但赋值仍在原地方
(2)函数声明同变量声明一样会提前;但是,函数表达式没有提前,就相当于平时的变量赋值
(3)函数声明会覆盖变量声明,但不会覆盖变量赋值:函数声明优先级高于变量声明的优先级;但是变量赋值以后,变量赋值初始化就会覆盖函数声明。
2、函数arguments属性初始化:
(1)callee:指向当前函数的引用
(2)length:真正传递的参数个数
(3)arguments的索引值小于传参的个数,则其值和实际传参的值是共享的;如果大于,则不共享,相方不相关,互不影响
3、and、or、not运算符详解:
And:(1)一个对象一个布尔,返回对象(2)2个都是对象返回第二个(3)某个是null或NaN,返回null或NaN(4)某个是undefined发生错误(5)2个都是布尔,返回布尔
Or:(1)一个对象一个布尔,返回对象(2)2个都是对象返回第一个(3)某个是null或NaN,返回null或NaN(4)某个是undefined发生错误
Not:(1)是对象,返回false(2)是0返回true,否则false(3)是null或NaN,返回true;是undefined发生错误
技巧:使用2个not运算符判断变量的布尔值(第一个not返回布尔值,再第二个not取反获得变量的真实布尔值)
4、性能优化:
(1)避免全局查找:用局部变量存储全局变量来减少全局查找,因为全局查找需要一直找到作用域链最顶端
(2)定时器:少用setTimeout,因为setTimeout每次都会初始化一个定时器,而setInterval只在开始的时候初始化一个定时器
(3)字符串连接少用+=,如果需要多次对同一个字符串进行+=操作的话,可以用数组来缓存,然后join方法连接
(4)数字转为字符串:"" + > String() > toString() > new String()
(5)浮点数转为整型推荐用Math.floor()或Math.round(),parseInt多用于将字符串转为数字
(6)多个变量声明,使用单var形式,以减少脚本执行时间
(7)多使用直接量更好,如:var array = [];
(8)使用文档碎片来构建dom结构:document.createDocumentFragment()
(9)使用innerHTML赋值代替构建dom元素
(10)尽量使用更准确的选择器,减少查找时间;对于获取兄弟元素,子集元素等也是一样的道理,尽量是定位更准确,避免循环
(11)删除dom节点之前,一定要删除注册在该节点上的事件,否则会产生无法回收的内存;
此外,在removeChild和innerHTML=""之间选择后者,因为用removeChild无法有效释放dom节点
(12)使用事件代理
(13)重复使用的结果,先保存到局部变量,避免多次取值的调用开销
(14)优化循环:减值迭代更高效、简化终止条件,避免重复取值、简化循环体、使用后测试循环(while比for高效)
(15)使用三目运算符替代条件分支
(16)闭包里变量的释放:返回前释放不再使用的变量:b = c = null;
5、页面重绘和回流:
(1)页面呈现流程:html代码解析成dom树、样式解析成样式结构体、dom树和样式结构体接回呈现渲染树、浏览器根据渲染树绘制页面
(2)回流:渲染树因为尺寸、布局、隐藏等变化而需要重新构建,称为回流
重绘:渲染树一些元素更新属性,但只影响外观风格,不影响布局,称为重绘
回流比将引起重绘,而重绘不一定引起回流
(3)引起重绘和回流的操作:任何对渲染树中元素的操作都会引起回流或者重绘。回流比重绘的代价要更高,回流的花销跟渲染树中有多少节点需要重新构建有关系
(4)聪明的浏览器,维护flush队列,一定时间批处理;
强制flush队列的一些属性
(5)减少重绘和回流:
一、不要一个一个改变样式,最好改变className,若需动态改变样式,使用classText拼接样式来改变
二、让操作的元素“离线处理”,处理完后一起更新:用createDocumentFragment或div来缓存dom元素、或先隐藏元素,处理完之后再显示元素
三、不要经常访问会引起浏览器flush队列的属性
四、将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位
6、高性能web开发:dom编程
(1)dom访问和修改:过桥例子
(2)html集合和遍历dom,暂存dom状态信息
(3)选择器越精确越好,缩小选择器的查找范围
7、JSONP:
(1)src属性拥有跨域的能力
(2)JSONP协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据(需要包裹json数据)
(3)ajax的核心是通过XmlHttpRequest获取非本页内容,
而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本。
8、解决跨域:
(1)利用JSONP格式:(1)url加:&callback=?;type变为"jsonp";(2)服务器端返回数据要为jsonp格式,即:out.print(callback + "(" + json + ")");要套一层callback()
(2)JS设置Header,实现跨域访问,服务器Action层增加Header头信息
9、form表单实现无跳转上传文件,接收页面后台数据
(1)form表单添加target属性,指定一个iframe的name,form表单提交后在iframe内嵌窗口接受响应,主页面就不会跳转
(2)让后台返回一个带执行函数的script标签
(3)另一种方法利用插件jquery-form的ajaxForm()方法
10、怎样避免重写可能已经定义了的方法。这可以通过在定义自己的方法之前,检测方法是否已经存在。
String.prototype.repeatify = String.prototype.repeatify || function(times) {/* code here */};
当你去扩展一个Javascript方法时,这个技术非常有用。
11、连续赋值的一个例子:
原理可解释为“就近原则”(后面的同名变量会覆盖前面的),而且要记住这种连续赋值,后面的变量是一种隐式声明即可。
12、数字精度丢失问题:前端遇小数计算概率大,要有这个精度丢失的意识;解决方案:把小数乘以倍数变成整数计算,再除以倍数变成原来的数。其中根据此思想封装了一个方法。
13、判断图片加载完成:
(1)img的onload事件
(2)img的complete属性,为true代表加载完毕
(3)readystatechange事件,没有onload事件和complete属性兼容性好
14、面向对象总结
(1)属性类型:是否可配置、枚举性(是否可for in循环)、可写性、可读性,默认true/true/true/undefined
(2)要修改属性默认的特性,需要通过Object.defineProperty()定义
(3)访问器属性:配置、枚举、get、set,其中get、set默认都是undefined,可以通过defineProperty来修改默认的get/set属性
(4)创建对象的设计模式:
工厂模式:用函数封装以特定接口创建对象
优点:可以无数次调用函数创建相似对象;
缺点:不能解决对象识别的问题
构造函数模式:this加属性、new关键字调用(new执行的4个步骤)
优点:解决了工厂模式无法对象识别的问题
缺点:创建了功能一样的函数,造成内存浪费
原型模式:创建每个函数的时候都有prototype属性,是一个指针,指向一个对象,该对象的用途就是包含由特定类型的所有实例共享的属性和方法
先创建一个空构造函数,然后通过prototype属性增加属性和方法,通过new关键字调用
优点:所有实例对象共享他的属性和方法(1、原型中的对象属性可以被实例所覆盖重写;2、通过delete可以删除实例中的属性,但是删除不了对象上的;3、hasOwnProperty()方法来确定一个属性是在原型上还是在实例上;4、简写模式,必须加constructor属性)
缺点:对于包含引用类型的属性来说,当修改的时候就是修改所有实例共享的那个值了,所有实例都会更改
组合使用构造模式和原型模式:构造模式定义实例属性,原型模式定义方法和共享的属性(实例所有的属性都是在构造函数中定义,而实例所有共享的属性和方法都是在原型中定义)
动态原型构造模式:写法更高效一些
(5)面向对象概念:一切事物皆对象;对象具有封装和继承特性;对象与对象之间使用消息通信,各自存在信息隐秘;JS基于原型来实现面向对象编程,而java基于类来实现面向对象编程
15、构造函数继承的6种方法
(1)构造函数绑定:使用call、apply将父对象的构造函数绑定在子对象上,即在子对象构造函数中加一行:Animal.apply(this,arguments)
(2)对象冒充:对象冒充的意思就是获取那个类的所有成员,然后调用,因为js是谁调用那个成员就是谁的
获取所有成员:this.stu = Stu; 然后调用this.stu(name,age);
(3)prototype模式:让prototype属性执行继承对象的一个实例。
注意2步:
第一步:将Cat的prototype对象指向一个Animal的实例:Cat.prototype = new Animal();(相当于完全删除了prototype对象原先的值,然后赋予一个新值)
第二步:重新纠正构造函数指向:Cat.prototype.constructor = Cat;
(任何一个prototype对象都有一个constructor属性,指向它的构造函数。如果没有"Cat.prototype = new Animal();"这一行,Cat.prototype.constructor是指向Cat的;加了这一行以后,Cat.prototype.constructor指向Animal。每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性。这显然会导致继承链的紊乱(cat1明明是用构造函数Cat生成的),因此我们必须手动纠正,将Cat.prototype对象的constructor值改为Cat。)
这是很重要的一点,编程时务必要遵守:即如果替换了prototype对象,那么,下一步必然是为新的prototype对象加上constructor属性,并将这个属性指回原来的构造函数
(4)直接继承prototype模式:Cat.prototype = Animal.prototype;缺点是Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype
(5)利用空对象做中介:
function F(){};
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;
F是空对象,所以几乎不占内存。这时,修改Cat的prototype对象,就不会影响到Animal的prototype对象。
(6)拷贝继承:把父对象的所有属性和方法,拷贝进子对象
16、非构造函数的继承
(1)object()方法:
第一步先在父对象的基础上,生成子对象:var Doctor = object(Chinese);
然后,再加上子对象本身的属性:Doctor.career;
(2)浅拷贝:把父对象的属性,全部拷贝给子对象,也能实现继承。缺点是引用类型的问题
(3)深拷贝:实现数组和对象的拷贝,递归调用"浅拷贝"即可
17、闭包的7种形式:
(1)函数作为返回值被返回,所以外界保持对里的引用
(2)函数赋值,上面的变形形式,将内部函数赋值给一个外部变量,外部变量保持了对内部函数作用域的引用
(3)函数参数传递函数的形式:(将F里的N作为参数给外界的Inner,F执行之后,外界Inner保持了对F里的引用)
(4)立即执行函数
(5)循环赋值
(6)迭代器
其实说到底闭包就是作用域外界保持了对作用域里面的引用
18、从执行环境看闭包和垃圾回收机制
代码一步步执行、执行环境、变量对象的活动状态和非活动状态、释放闭包引用,脱离执行环境
19、封装组件思想:
(1)通过$.extend去扩展原生的jquery.ajax
得到$.ajax对象
每次调用发送请求时定义默认的error等处理方法
如果在调用时写了error方法就不用默认的
利用extend扩展原生的$.ajax方法,返回最新的参数
将最新的参数返回给ajax对象
即:先定义默认参数、再判断用户是否自定义参数、$.extend()方法拓展默认参数列表
(2)自定义组件步骤:
定义jquery拓展方法,$.fn.combobox = function(options,param){}
设置默认参数列表
将调用时传递的参数和默认参数对象合并,拓展默认参数列表
给元素添加默认值
判断用户传过来的参数列表里面是否包含数据data数据集,如果包含,直接用data,不用发ajax从后台取,否则发送ajax从后台取数据
如果传过来的不是对象,是字符串,代表调用方法
20、不可变对象实现
(1)const
关键字只是修改了某个变量名和其值之间的链接,而不是实际值。所以通过const声明的对象,我们依然可以改变
(2)Object.freeze()使对象的原始属性不可变,但是我们仍然可以更改嵌套对象
(3)最终方案:采用递归freeze()所有嵌套对象