2020.04.03 Javasrcipt专项练习错误总结
一、
关系不大
二、
块内声明的变量只要没加var 都算作全局变量。
js看起来像支持块级作用域,实际上只有函数作用域和全局作用域。
JavaScript语言精粹内对全局变量有明确的批判
A:函数声明的形式定义函数有声明提升的特性,也就是不论是否进入if(x),foo都定义了
C,声明一个全局函数foo
D,ES6才规定块级作用域。
不要在块内声明一个函数(严格模式会报语法错误)。如果确实需要在块中定义函数,可以使用函数表达式来声明函数。
三、
function fun1(){
var val; console. log(val);//undefined
val = 20; console.log(val);//20
}
fun1();
解析:由于函数内重新定义val,导致变量val被提升到了顶端,但它被赋予的值并没有提升,所以第一个console.log(val)输出的只是定义过但没有被赋值的val,第二个console.log输出的是被赋值20的val。
变量提升,赋值语句不提升
var val = 20 等价于 var val ,val = 20
所以第一个console.log(val)只能读取到val而读取不到20
js里有域的概念,函数外边的是全局值,在fun1函数里是局部域,可以读取外部变量,但是这个局部域里又重新定义,就不会再读取外部,覆盖
四、
concat()连接两个或者更多的数组并返回结果。pop()删除并返回数组的最后一个元素。sort()是对数组进行排序,splice()删除元素并向数组中添加新元素。
concat连接数组但是不改变院数组,splice删除数据会改变数组,sort排序会改变数组,pop出栈返回最后一个最后一个元素,改变数组
五、
|
程序的输出是什么?bar bar undefined bar
解析:
1.第一个this.foo输出bar,因为当前this指向对象myObject。
2.第二个self.foo输出bar,因为self是this的副本,同指向myObject对象。
3.第三个this.foo输出undefined,因为这个IIFE(立即执行函数表达式)中的this指向window。
4.第四个self.foo输出bar,因为这个匿名函数所处的上下文中没有self,所以通过作用域链向上查找,从包含它的父函数中找到了指向myObject对象的self。
// 以下的任何一种方式都可以立即执行函数表达式,利用函数的执行环境 // 创建私有作用域 (function(){ /* code */ }()); // Crockford 推荐这个 (function(){ /* code */ })(); // 这个同样运行正常
六、
function Foo(){
var i=0;
return function(){
console.log(i++);
}
}
var f1=Foo(), f2=Foo();
f1();//return i=1 输出0
f1();//return i=2 输出1
f2();//return i=1 输出0
解析:
f1(),f2()分别创建了自己的执行环境,所以它们两个是相互独立的,执行之后都会返回一个匿名函数,这个匿名函数的作用域链被初始化为其包含函数的活动对象(这里也就是i)和全局变量对象,f1执行之后i并不会销毁,因为返回的匿名函数还要引用i,i仍然在内存中,所以执行两次之后i的值变成了1,而f2执行之后i为0
++运算符的使用,放在数字后表示后增,即先执行再+1,这里就是先执行console.log(i)之后i再加1,所以f1执行第二次的时候i已经变成了1,自然就输出1了。
七、
立即执行函数(IIFE)不是一种闭包,它只是函数的一种调用方式,和闭包没有必然的联系,两者经常结合着一起使用,但两者的本质并不同。
链式作用域是js本身自然存在的一种获取变量的方式。链式作用域外部可以访问内部,内部访问外部需要设置一个函数,由此产生闭包。
C:闭包可以访问其他函数内部变量的函数,而立即执行函数穿参,相当于父级变量;闭包需要手动执行,不能自主释放内存;而立即执行函数相当于做了一次函数执行,最后释放内存。逻辑上立即执行函数可以理解为:声明闭包函数-执行-释放内存
八、
先解释一下“同步模式”和“异步模式”:
(1)同步模式:就是后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的。
(2)异步模式:完全不同,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。
JavaScript中实现异步编程模式的4种方法,回调函数、事件监听、发布/订阅、Promises对象:
(1)回调函数:这是异步编程最基本的方法,优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合(Coupling),流程会很混乱,而且每个任务只能指定一个回调函数。 例:假定有两个函数f1和f2,后者等待前者的执行结果,如果f1是一个很耗时的任务,可以考虑改写f1,把f2写成f1的回调函数。
(2)事件监听:任务的执行不取决于代码的顺序,而取决于某个事件是否发生。优点是比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以”去耦合”(Decoupling),有利于实现模块化。缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。
例:为f1绑定一个事件,当f1发生done事件,就执行f2。
(3)发布/订阅:我们假定,存在一个”信号中心”,某个任务执行完成,就向信号中心”发布”(publish)一个信号,其他任务可以向信号中心”订阅”(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做”发布/订阅模式”(publish-subscribe pattern),又称”观察者模式”(observer pattern)。这种方法的性质与”事件监听”类似,但是明显优于后者。因为我们可以通过查看”消息中心”,了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。
(4)Promises对象:是CommonJS工作组提出的一种规范,目的是为异步编程提供统一接口。简单说,它的思想是,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。回调函数变成了链式写法,程序的流程可以看得很清楚,而且有一整套的配套方法,可以实现许多强大的功能。
例:f1的回调函数f2,f1().then(f2);
代码例子:
1.回调函数
f1();
f2();
function f1(callback){
setTimeout(function () {
// f1的任务代码
callback();
}, 1000);
}
f1(f2);
2.事件监听
f1.on('done', f2);
function f1(){
setTimeout(function () {
// f1的任务代码
f1.trigger('done');
}, 1000);
}
3.发布/订阅
jQuery.subscribe("done", f2);
function f1(){
setTimeout(function () {
// f1的任务代码
jQuery.publish("done");
}, 1000);
}
jQuery.unsubscribe("done", f2);
4.Promises对象
f1().then(f2);
function f1(){
var dfd = $.Deferred();
setTimeout(function () {
// f1的任务代码
dfd.resolve();
}, 500);
return dfd.promise;
}
指定多个回调函数:
f1().then(f2).then(f3);
指定发生错误时的回调函数:
f1().then(f2).fail(f3);
九、
d.setDate(n);
n表示一个月中的一天的一个数值(1 ~ 31):
0 为上一个月的最后一天
-1 为上一个月最后一天之前的一天
如果当月有 31 天:
32 为下个月的第一天
如果当月有 30 天:
32 为下一个月的第二天;
40 为下一个月的第9天;
setMonth(n),这里是0-11分别对应1-12月