JavaScript
1、函数与数据类型
[Undefined,Null,Boolean,Number,String,Object]
(1)var 预编译,当前作用域内的变量声明被提前到作用域顶部,但赋值保留在原处。
(2)函数声明提前分两种
函数声明
function a();
函数表达式
var a = function();
只有var a被提前; //还是undefined,不能先调用。
(3) Null==undefined
null是一个object, undefined是一个windows对象,Undifeind
(4)method.call(this, arg1,arg2)
, method.apply(obj,[arg1,arg2..])或者直接是arguments对象。
两者的作用都是改变函数的运行环境,执行上下文。[1]改变函数执行上下文,将this指向一个obj对象。
(5)匿名函数。
(function(a,b))
–匿名函数的引用,
(function(a,b))(c,d)
函数的调用,jQuery就是一个匿名函数。
全局变量是魔鬼,匿名函数执行一次就被销毁了,不会在内存中驻留,不会造成全局变量污染。
注意与闭包的区别,其实两者就没关系。
2、 Javascript作用域链
(1)JavaScript并没有块及作用域,只有函数级作用域:变量在声明它们的函数体及其子函数内是可见的。函数唯一拥有自身作用域的结构(for 循环没有自身作用域)。
(2)代码在一个环境中执行的时候,会创建变量对象的一个作用域链,来保证变量的有序访问,作用域链的第一个对象是当前执行的环境,下一个是父函数的执行环境—>直到最外边一个全局作用域,这个链叫作用域链。
(3)js解析是沿着作用域链一级一级搜索的过程,从第一个对象开始,逐级向后回溯直到window对象,找到同名标识符停止,找到后不再继续遍历,找不到就报错。父函数定义的变量在子函数的作用域链中,子函数没有被销毁,其作用域链中所有变量和函数就会被维护,不会被销毁(闭包的原理)
3、 javascript原型继承与原型链
(1)每个函数都有一个prototype属性(new出来的普通对象是_proto_
属性),都会指向该对象的原型对象Person.prototype
. 当我们用构造函数new一个实例后,从原型对象继承属性和方法。
(2)为什么prototype?因为不prototype话,new两个对象,对象里的属性和方法同名,但确实不同的引用,对属性来说,没问题,但是对同名的方法,功能是一样,却一直在复制一样的东西,这就要prototype。两个数组push,一个操作不影响另外一个。自己的属性和方法在构造器中用this,公用的方法用prototype;
(3)原型链:当代码去读取某个对象的属性是,会执行一遍搜索,先从对象本身的属性查找,如果没有,则从对象的原型对象查找,在没有原型对象prototype上查找。。。知道
object原型上没有就报错,因为object的原型是null。
p—p原型—p原型的原型-obj hasOwnProperty()
(4)继承:子类继承父类的属性和方法,将父类的实例复制给子类的原型,或者调用apply或者call方法。
4、 闭包(闭的是谁?)
(1)闭包是指在 JavaScript 中,内部函数总是可以访问其所在
的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。内部function会闭外部function的局部变量直到内部function结束。这意味着当前作用域总是能够访问外部作用域中的变量。举个实例吧:
function a() {
var i = 0;
function b() { alert(++i); }
return b;
}
var c = a();
c();
原理:根据js垃圾回收机制,标记清除,引用清除嘛。
利用这个特性,刚好 a–>b —>c引用着。这样被引的着就不会被回收了,因为b的执行,依赖a,所以a的变量,没有被回收。
(2)好处。
- [1]避免全局变量的污染,因为不加var就是全局变量,这样的临时变量是GV,有很多坏处,有些只要执行一次,组件初始化。我们可以用闭包,构建匿名自执行函数,执行完很快就被释放了。return该要的东西就行。
- [2]缓存,一个处理很耗时的函数对象,每次调用都花费很好的时间,我们只要将计算出来的值存储起来,当下次调用的是要,先查找,有就直接用,没有再计算并存起来。闭包刚好,利用这一点,因为他不会释放外部的引用,内部函数的值得以保存。
- [3]实现封装,防止变量跑到外面的作用域,造成命名冲突(和第一点类似)。
- [4]模拟面向对象中的对象
function Person(){
var name = "default";
return {
getName : function(){
return name;
},
setName : function(newName){
this.name = newName;
}
}
};
(3)坏处,
- [1]内存消耗,一般函数的活动对象会随着执行上下文一起销毁,但是闭包引用另外一个函数的活动对象,因此对象无法被销毁。垃圾回收是根据引用标记来销毁对象,如果最后一个属性没有被引用才会销毁改对象,这也意味着闭包需要更多的内存消耗。IE浏览器中,如果存在循环应用,可能会造成内存泄露的问题。
- [2]闭包可能引起跨域访问,影响性能。如果一个跨作用域的对象被引用了一次以上,则先把它存储到局部变量里再使用。Var doc = document;
5、js事件流
从页面上接收到事件的顺序。IE事件流是事件冒泡,W3C事件流刚好相反是事件捕获
事件冒泡:假如单击了内层标签按钮,他会一层一层网上走,直到document。
事件捕获: 相反,从外到内,document->-body->div1-div2.
但是要使支持事件捕获addEventListener(‘click‘,function,true);
第三个参数为true,默认false为事件冒泡。
(1) 事件处理的三个阶段:事件捕获,目标阶段,事件冒泡阶段,IE8不支持mp.
(2) 一个标签绑定多个事件
- IE: attachEvent(“onclick”,function) – detach先绑定后触发,
- W3C: addEventListener(“click”,function, false) remove先绑定先触发。
区别btn.onclick=function 作用域是this—当前dom节点。attachEvent()中的this是windows.
(3)兼容性代码:
- w3C事件对象是:event IE:windows.event
- 事件目标event.target IE:windows.event.srcElement |currentTarget:当前正在处理的元素 target:目标元素
- 取消默认行为:event.returnValue = false; IE:preventDefault
- 事件冒泡:event.stopPropagation || event.cancleBubble = true;
function addEvent(obj, type , callback){
if(windows.attachEvent{
obj.attachEvent(“on”+type,callback)}
else{
obj.addEventListener(type,callback)
}
}
function stopBubble(event){
if(windows.event)
{
event.cancleBubble = true;
}
else{
event.stopPropagation
}
}
var table = document.getElementById( "table" );
table.onclick = function( e ){ var e = e || window.event;
var target = e.srcElement || e.target;
if( target.tagName.toUpperCase() == "td" ){ …. };
(4)事件委托/代理:利用事件冒泡的原理,把事件处理器添加到1父级元素上
优:(1)可以节省大量内存的占用,减少事件注册,table—代理td
(2)新增子元素时,无需进行事件保定,动态部分很适合。
缺:不能什么都代理,很可能事件误判,劲酒虽好,不要贪杯哦。
6、Es6新特性
转译工具Traceur ,Babel 编译成ES5. 你们现在的项目中有用SE6的这些新特性吗?
(1)一些新增的api. [“a”,”b”,”c”].keys(),values(),entries()
和map一样了。有集合Set, Map
(3)生成器genrators, function* qq(name){yield 暂停,}
。next()
方法执行下一句。
(4)可变参数—代替arguments Map.set(document,”the document”);
for (var [key,value] of map){…}
(5)箭头 Input(function的参数)=>Output(return…)
不要function,也不要return了array.filter(obj => job.isSelect())
写一些回调函数的时候很好用array.filter(function(job){ return job.isSelect()});
(6)有代理Proxy, 还有类Class. 模块Moudules export import
导入 导出模块。
(7)有let
,定义块级作用域,不会像var一样,作用域是函数体的全部,比如for 循环的变量i是共享的。
let是更完美的var,(1)有块级作用域,let最外面声明的不是全局变量,windows访问不到,let不能重定义。
const用于声明长粮食,在声明的时候赋值,不能修改。
7、 设计模式
js开发前端UI组件中会经常涉及,纯粹的页面业务逻辑可能涉及不多。公开出哪些方法,应该提供 哪些接口,哪部分逻辑流程应该开放出去让用户自行编写。如何实现高内聚低耦合,如何实现组件的高复用。常用的一些设计模式
- (1)单例模式:划分命名空间很好用。Var XGP={Util:{},CSS:{},Ajax:{}} 惰性单体:懒加载,return 判断没有在创建实例
(2)观察者模式: (3)职责链模式: (4)工厂模式:
8、组件开发
问题(1)复用性:通过继承来复用。改变的是一点点。属性:父类.call(this,) 方法继承原型=new 父类();
遇到的问题:(2)不同组件的传参是不一样的,怎么弄? 传递的顺序也不一样,构造一个json配置参数,有则用配置的参数,没有就用默认的。extend( this.settings , opt )
;
9. Js函数节流
主要思路是,通过一个定时器,阻断连续重复的函数调用。
特征:1. 短时间内连续多次重复触发 ; 2. 频繁的 DOM 操作,操作DOM代价大,在用户察觉范围外,降低函数调用的频率,从而提升性能。
function(){
clearTimeout(timer);
timer = setTimeout(function () {
}, 100)};
JS模块化—-require.js/sea.js
优点:
(1) 防止js加载阻塞页面渲染。 Js中有alert();
(2) 更好的分离。一堆script脚本, html中只需引入一个主入口就好了。然后在依赖进来
(3) 更好的组织代码,大型项目的维护,多人开发,代码风格不一样,业务逻辑混杂,一个文件一个模块,这样控制了文件的粒度,一个萝卜一个坑。单一职责原则,体现OOP思想
(4) 按需加载,虽然js压缩在一个js中可以减少请求,但是只需依赖少数几个文件时,体积与数量的折中
(5) 避免命名冲突,闭包,匿名自执行函数, 模块化只向外面暴露该模块的接口。
(6) 更好的依赖管理,A依赖B,用A之前要script加载好B,如果过段时间不依赖B,依赖C,很难维护的。
缺点:
(1).系统分层,调用链会很长
(2).模块间通信,模块间发送消息会很耗性能 。如果有很多jquery插件,那这样要是实现一个很复杂的交互时,模块间的依赖会很多
requriejs 异步加载,依赖前置,提前执行
AMD是requirejs的模块化定义的的规范,CMD是sea.js模块化规范。
define 定义模块
define([‘require’,’foo’],function(){return}); baseUrl + paths
require加载模块 require([‘foo’,’bar’],function(foo,bar){});
非AMD模块输出,插件不符合符合AMD规范。 用功能:shim require.config({ shim{‘chajian’:exports|| deps:}})
Sea.js —同步加载,依赖就近,延迟执行
define 定义, exports导出,对外提供单个接口, define(function(require,exports,module){});
module上存储了当前模块的一些对象,可以直接导出一个对象。module.exports =a; 提供整个接口
require(./a)
直接引入, require.async
异步引入。 这就是 CMD 模块定义规范的所有内容。