一、对闭包的理解,实现一个暴露内部变量,而且外部可以访问修改的函数(get和set,闭包实现)
Q1:闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。
ECMAScript中对闭包的解释:
允许使用内部函数(即函数定义和函数表达式位于另一个函数的函数体内),而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其它内部函数。当内部函数在包含它的外部函数之外被调用时,就会形成闭包。即内部函数在外部函数返回后也可以被执行。当内部函数执行时,它访问的外部函数的局部变量、参数和函数声明是外部函数返回时的值,但也会受到内部函数的影响。
Q2:
var person=(function(){
var name='xiaoming';
return{
getName:function(){
return name;
},
setName:function(newName){
name=newName;
return newName;
}
};
})();
console.log(person.name);
console.log(person.getName());
console.log(person.setName('xiaohua'));
二、{}=={}? []==[]? null==undefined?
console.log({}=={});//false
console.log([]==[]);//false
console.log(null==undefined);//true
[] == [] ?这个好理解. 当两个值都是对象 (引用值) 时, 比较的是两个引用值在内存中是否是同一个对象. 因为此 [] 非彼 [], 虽然同为空数组, 确是两个互不相关的空数组, 自然 == 为 false.
{}=={}? 同上。
[] == ![] 这个要牵涉到 JavaScript 中不同类型 == 比较的规则, 具体是由相关标准定义的. ![] 的值是 false, 此时表达式变为 [] == false, 参照标准, 该比较变成了 [] == ToNumber(false), 即 [] == 0. 这个时候又变成了 ToPrimitive([]) == 0, 即 '' == 0, 接下来就是比较 ToNumber('') == 0, 也就是 0 == 0, 最终结果为 true.
参考文献:[] ==[] 为 false;[] == ![] 为 true;[] == {} 为 false;为什么?
三、基本的数据类型
String,Boolean,Number,Null,Undefined
四、手写一个递归函数(考察arguments.callee,以及arguments的解释)
例:求函数阶乘
function factorial (num){
if(num<=1){
return 1;
}else{
return arguments.callee(num-1)*num;
}
}
arguments.callee是指向正在执行的函数的指针,可以用它来实现对函数的递归调用。
标识符arguments是指向实参对象的引用,实参对象是一个类数组,可以通过数字下标访问传入函数的实参值。(省略的实参都是undefined,多出的参数自动省略。)
注意:arguments.callee只能用于非严格模式下,在严格模式下,不能通过脚本访问arguments.callee。可以使用命名函数表达式来解决。
格式:var 变量=(函数f);//即把函数赋值给了另一个变量,函数的名字f仍然有效,所以递归调用可以照样完成。
var factorial=(function f(num){
if(num<=1) {return 1;
}else{
return f(num-1)*num;
}
});
参考文献:《JavaScript高级程序设计》
五、对前端路由的理解?前后端路由的区别?
路由:根据不同的url地址展示不同的内容或者页面。
Q1:
前端路由:把不同的路由对应不同的内容或页面的任务交给前端来做。
应用场景:单页面应用,大部分页面结构不变,只改变部分内容的使用。
实现方式:
1)改变hash值,监听onhashchange事件。
优点:兼容低版本浏览器
2)使用historyAPI,监听popState事件,用pushState和replaceState来实现。
Q2:
后端路由每次访问新页面需要向服务器重新发送请求,服务器再响应请求,这个过程可能会有延迟。
前端路由访问新页面时仅仅是变换路径,没有网络延迟。
参考文献:
前端路由与后端路由
关于前端路由和后端路由的一点思考
六、前后端分离的意义以及对前端工程化的理解
Q1:
1、项目一开始制作前端页面时,不再需要后台配置服务器环境。
2、前端不需要向后台提供模板,或是后台在前端html中嵌入后台代码。
3、后台没有时间提供接口时,前端可以将数据先写死或者调用本地的json文件即可。
4、页面的增加和路由的修改可以在前端实现,开发更加灵活。
5、通过前端路由配置,我们可以实现页面的按需加载,无需一开始加载页面便加载网站的所有资源,服务器也不再需要解析前端页面。
6、通过目前主流的MVC框架,我们可以非常快速的定位及发现问题的所在,客户端问题不再需要后台人员参与及调试,代码重构及可维护性强。
Q2:
完整的前端工程体系应该包括:
1、统一的开发规范;
2、组件化开发;
3、构建流程。
参考文献:
我们为什么要尝试前后端分离
浅析前端工程化
七、手写类式继承并解释。
3.类式继承(适合new构造函数)
利用构造函数继承的方式。
JS中没有类的概念,把JS的构造函数看做类。
Paste_Image.png
function Father(){
this.name='晓明';
}
Father.prototype.showName=function(){
alert(this.name);
};
function Son(){}
Son.prototype=new Father();//一句话实现继承,但存在很多问题
var s1=new Son();
s1.showName();//晓明
alert(s1.constructor);//Father
存在问题:
1.person.constructor指向Father而不是Son.
这是因为Son.prototype=new Father();这句话重写了Son.prototype,Son.prototype下原有的constructor等属性被覆盖。
解决:修正Son.prototype.constructor,Son.prototype.constructor=Son;
2.子类实例的属性均指向父类中的属性,会相互影响。
解决:属性和方法分别单独继承。
Paste_Image.png
1)创建一个没有属性的空函数F,专门用来继承父类的方法。
2)属性采用call方法继承。
类式继承完整例子:
function Father(){
this.name=[1,2,3];
}
Father.prototype.showName=function(){
alert(this.name);
};
function Son(){
Father.call(this);//属性继承,用call方法修改this指向
}
//方法继承
var F=function(){};//创建一个没有属性的空函数F,专门用来继承父类的方法。
F.prototype=Father.prototype;
Son.prototype=new F();//一句话实现继承
Son.prototype.constructor=Son;
var s1=new Son();
s1.name.push(4);
s1.showName();//1,2,3,4
var s2=new Son();
alert(s2.name);//1,2,3。属性单独继承后,不同实例的属性相互独立,互不影响。
八、js轮播实现思路
图片轮播的原理就是图片排成一行,然后准备一个只有一张图片大小的容器,对这个容器设置超出部分隐藏(overflow:hidden),在设定定时器或点击左右方向键来让这些图片整体左移或右移,这样呈现出来的效果就是图片在轮播。
九、(==)和(!=)运算规则
1、当两个操作数类型相同时,直接比较,相等返回true,不相等返回false。
2,、当两个操作数类型不同时,先转换成相似类型再进行比较。
转换规则:
1)操作数是布尔值,比较之前先转换为数值。false转换为0,true转换为1。
2)操作数是字符串和数值,比较之前先将字符串转换为数值。
3)操作数是对象和非对象,调用对象的valueof()方法,用得到的基本类型值进行比较或按照前面的规则进行转换。
比较规则:
1)null==undefined
2)比较相等性之前,不能将null或者undefined转换成其他任何值。
3)有一个操作数为NaN,则==结果为false,!=结果为true。NaN!=NaN
4)两个操作数都是对象,仅当两个操作数指向同一个对象时返回true,否则,返回false。
十、文档模式doctype
1、混杂模式:该模式下IE的行为与IE5相同。文档开始处没有发现文档类型声明或者文档类型声明有错误,所有浏览器会默认开启混杂模式。在混杂模式下,不同浏览器的行为差别很大,故不推荐。
2、标准模式:该模式下IE的行为更接近标准行为。
①严格型(strict)
②过渡型(transitional)
③框架集型(frameset)