1、立即执行函数表达式是什么?有什么作用
我们都知道,一般定义一个函数有函数声明和函数表达式两种方法:
function fnName () {…}; //函数声明
var fnName = function () {…}; //函数表达式
两者的区别是:
- Javascript引擎在解析javascript代码时会‘函数声明提升'(Function declaration Hoisting)当前执行环境(作用域)上的函数声明,而函数表达式必须等到Javascirtp引擎执行到它所在行时,才会从上而下一行一行地解析函数表达式。
- 函数表达式后面可以加括号立即调用该函数,函数声明不可以,只能以fnName()形式调用 。
所以,要在函数体后面加括号就能立即调用,则这个函数必须是函数表达式,不能是函数声明。
在function前面加()、!、+、-、=等运算符,都将函数声明转换成函数表达式,消除了javascript引擎识别函数表达式和函数声明的歧义,告诉javascript引擎这是一个函数表达式,不是函数声明,可以在后面加括号,并立即执行函数的代码。
(function(){
console.log(123)
})() //输出123
(function(){
console.log(123)
}()) //输出123
!function(){
console.log(123)
}() //输出123
+function(){
console.log(123)
}() //输出123
-function(){
console.log(123)
}() //输出123
var a=function(){
console.log(123)
}() //输出123
加括号是最安全的做法,因为!、+、-等运算符还会和函数的返回值进行运算,有时造成不必要的麻烦。
那么这样做有什么作用:
在全局或局部作用域中声明了一些变量,可能会被其他人不小心用同名的变量给覆盖掉,根据javascript函数作用域链的特性,可以使用这种技术可以模仿一个私有作用域,用匿名函数作为一个“容器”,“容器”内部可以访问外部的变量,而外部环境不能访问“容器”内部的变量,所以( function(){…} )()内部定义的变量不会和外部的变量发生冲突,俗称“匿名包裹器”或“命名空间”。
2、求n!,用递归来实现
利用n!等于n(n-1)!,(n-1)!等于(n-1)((n-1)-1)!,直至括号内的值为1,另外0!等于1。
function fac(n){
if( n===1 || n===0 ){return 1}
return (n*fac(n-1))
}
console.log(fac(1)) //输出1
console.log(fac(2)) //输出2
console.log(fac(3)) //输出6
console.log(fac(4)) //输出24
console.log(fac(5)) //输出120
也可以用三元运算符 ' ? ' 写作:
function fac(n){
return n===0 || n===1 ? 1 : n*fac(n-1)
}
console.log(fac(5)) //输出120
另外也可以利用循环来处理
function fac(n){
var i =1
if( n===1 || n===0 ){console.log(1)}
else{
for( var j=n;j>1;j--){
i*=j
}
console.log(i)
}
}
fac(5) //输出120
3、以下代码输出什么?
function getInfo(name, age, sex){
console.log('name:',name);
console.log('age:', age);
console.log('sex:', sex);
console.log(arguments);
arguments[0] = 'valley';
console.log('name', name);
}
getInfo('饥人谷', 2, '男');
getInfo('小谷', 3);
getInfo('男');
注意:
- console.log(…)输出的并不是括号内的返回值,而是输出括号内所有表达式的值。
- 给函数传入参数时是按顺序传入,不会自动识别,没有传入参数则为undefined。
所以getInfo('饥人谷', 2, '男');
相当于:
function getInfo(){
arguments[0]='饥人谷'
arguments[1]=2
arguments[2]='男'
console.log('name:','饥人谷')
console.log('age:', 2)
console.log('sex:', '男')
console.log(['饥人谷',2,'男'])
arguments[0] = 'valley'
console.log('name', 'valley')
}
getInfo()
/*输出:
name: 饥人谷
age: 2
sex: 男
["饥人谷", 2, "男"]
name valley
*/
getInfo('小谷', 3);相当于
function getInfo(){
arguments[0]='饥人谷'
arguments[1]=3
arguments[2]=undefined
console.log('name:','饥人谷')
console.log('age:', 3)
console.log('sex:', undefined)
console.log(['饥人谷',3])
arguments[0] = 'valley'
console.log('name', 'valley')
}
getInfo()
/*输出:
name: 饥人谷
age: 3
sex: undefined
["饥人谷", 3]
name valley
*/
getInfo('男');相当于
function getInfo(){
arguments[0]='男'
arguments[1]=undefined
arguments[2]=undefined
console.log('name:','男')
console.log('age:', undefined)
console.log('sex:', undefined)
console.log(['男'])
arguments[0] = 'valley'
console.log('name', 'valley')
}
getInfo()
/*输出:
name: 男
age: undefined
sex: undefined
["男"]
name valley
*/
四、 写一个函数,返回参数的平方和?
function sumOfSquares(){
}
var result = sumOfSquares(2,3,4)
var result2 = sumOfSquares(1,3)
console.log(result) //29
console.log(result2) //10
解答思路:遍历每一个传入的参数,求它们的平方和;通过不同遍历的方法,有不同的写法。
- for循环方法
循环每执行一次,都要检查一次 array.length 的值,读属性要比读局部变量慢,尤其是当 array 里存放的都是 DOM 元素(像 array = document.getElementByClassName(“class”);),因为每次读 array.length 都要扫描一遍页面上 class=”class” 的元素,速度更是慢得抓狂。
function sumOfSquares(){
var sum=0
for(var i=0;i<arguments.length;i++){
sum+=arguments[i]*arguments[i]
}
return sum
}
var result = sumOfSquares(2,3,4)
var result2 = sumOfSquares(1,3)
console.log(result) //29
console.log(result2) //10
- for-in循环方法
for-in 需要分析出 array 的每个属性,这个操作的性能开销很大
function sumOfSquares(){
var sum=0
for(i in arguments){
sum+=arguments[i]*arguments[i]
}
return sum
}
var result = sumOfSquares(2,3,4)
var result2 = sumOfSquares(1,3)
console.log(result) //29
console.log(result2) //10
- 先把数组的长度先查出来,存进一个局部变量,那么循环的速度将会大大提高
function sumOfSquares(){
var sum=0
var length=arguments.length
for(var i=0;i<length;i++){
sum+=arguments[i]*arguments[i]
}
return sum
}
var result = sumOfSquares(2,3,4)
var result2 = sumOfSquares(1,3)
console.log(result) //29
console.log(result2) //10
- 不过我们还可以让它更快。如果循环终止条件不需要进行比较运算,那么循环的速度还可以更快
- 把数组下标改成向 0 递减,循环终止条件只需要判断 i 是否为 0 就行了。因为循环增量和循环终止条件结合在一起,所以可以写成更简单的 while 循环
function sumOfSquares(){
var sum=0
var i=arguments.length
while(i--){
sum+=arguments[i]*arguments[i]
}
return sum
}
var result = sumOfSquares(2,3,4)
var result2 = sumOfSquares(1,3)
console.log(result) //29
console.log(result2) //10
5、如下代码的输出?为什么
console.log(a);
var a = 1;
console.log(b);
由于变量提升的原则,上述代码相当于
var a
console.log(a) //输出undefined
a=1
console.log(b) //报错:Uncaught ReferenceError: b is not defined
- 原因:先声明了变量a,a并没有复制,所以此时输出a得到undefined;b没有声明就直接调用,所以会报错。
6、 如下代码的输出?为什么
sayName('world');
sayAge(10);
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
输出:"hello" "world"
Uncaught TypeError: sayAge is not a function
- 原因:由于函数声明会自动提升,而函数表达式不会;
sayName('world');
正常执行;sayAge(10);
会调用函数sayAge
但此时只声明了sayAge
是变量,并未将函数声明赋值给它,所以它还不是函数,所以报错。
7、 如下代码输出什么? 为什么
var x = 10
bar()
function foo() {
console.log(x)
}
function bar(){
var x = 30
foo()
}
输出:10
- 原因:函数的作用域与其定义时所在的作用域有关,与其调用时所在的作用域无关;在
bar()
中可以调用全局作用域中的foo()
,而foo()
不能访问bar()
的局部变量x(x=30),只能访问全局作用域中的全局变量x(x=10),所以输出10。
8、如下代码输出什么? 为什么
var x = 10;
bar()
function bar(){
var x = 30;
function foo(){
console.log(x)
}
foo();
}
输出:30
- 原因:变量的查找是就近原则,去寻找var定义的变量,当就近没有找到的时候就去查找外层。函数域优先于全局域,故局部变量x会覆盖掉全局变量x,所以输出30。
9、如下代码输出什么? 为什么
var a = 1
function fn1(){
function fn2(){
console.log(a)
}
function fn3(){
var a = 4
fn2()
}
var a = 2
return fn3
}
var fn = fn1()
fn() //输出多少
输出:2
- 原因:先执行fn1,返回值是fn3的函数表达式,此时fn1的局部作用各变量已经赋值完毕;将fn3赋值给fn,即
fn=function (){var a=4;fn2()}
;执行fn,调用执行fn2,fn2查找变量a为2,所以输出2。
10、如下代码输出什么? 为什么
var a = 1
function fn1(){
function fn3(){
var a = 4
fn2()
}
var a = 2
return fn3
}
function fn2(){
console.log(a)
}
var fn = fn1()
fn() //输出多少
输出:1
- 原因:先执行fn1,返回值是fn3的函数表达式,此时fn1的局部作用各变量已经赋值完毕;将fn3赋值给fn,即
fn=function (){var a=4;fn2()}
;执行fn,调用执行fn2,fn2查找变量a为1,所以输出1。
11、如下代码输出什么?为什么
var a = 1
function fn1(){
function fn3(){
function fn2(){
console.log(a)
}
fn2()
var a = 4
}
var a = 2
return fn3
}
var fn = fn1()
fn() //输出多少
输出:undefined
- 原因:先执行fn1,返回值是fn3的函数表达式,此时fn1的局部作用各变量已经赋值完毕,但fn3中的变量并没有赋值;将fn3赋值给fn,即
fn=function (){function(){console.log(a)};fn2();var a=4}
;执行fn,调用执行fn2,fn2查找变量a,由于变量提升,此时a已声明但没有赋值,所以输出undefined。
12、.如下代码输出什么?为什么
var obj1 = {a:1, b:2};
var obj2 = {a:1, b:2};
console.log(obj1 == obj2); //输出false
console.log(obj1 = obj2); //输出{a:1, b:2}
console.log(obj1 == obj2); //输出true
-
console.log(obj1 == obj2)
因为此时obj1与obj2在内存中堆存储位置不同,所以不相等,输出false -
console.log(obj1 = obj2)
先把obj2的引用赋给obj1,此时它们在堆内存存储位置相同,这里输出obj1本身,所以输出{a:1, b:2} -
console.log(obj1 == obj2)
此时obj1和obj2是相同的引用,所以相等,输出true。
13、如下代码输出什么? 为什么
var a = 1
var c = { name: 'jirengu', age: 2 }
function f1(n){
++n
}
function f2(obj){
++obj.age
}
f1(a)
f2(c)
f1(c.age)
console.log(a) //输出 1
console.log(c) //输出 {name: "jirengu", age: 3}
- f1(n)中是通过参数传递基本类型,f1(a)把a=1赋值给n,++n并不影响a的值
- f2(obj)中是通过参数传递引用类型,f1(c)把c的引用传递给obj,obj.age发生改变,c.age也要改变。
14、写一个深拷贝函数
思路:深拷贝的难点是对对象中嵌套的对象进行深拷贝,而不是直接引用,如嵌套的对象、数组、函数等typeof返回值为object的属性或方法;但是null除外,虽然typeof null返回object,但是他是一个基本类型,不需要,深度拷贝。
利用typeof,对对象深拷贝:
var soldier1 = {
name: '侦察兵',
age: 3,
equipment:{rifle:'AK-47',pistol:'desertEagle',knife:null},
family:['father','sister'],
other:null
}
function deepCopy(oldObj) {
var newObj = {}
for(var key in oldObj) {
if(typeof oldObj[key] === 'object' && oldObj[key]) {
newObj[key] = deepCopy(oldObj[key])
}else{
newObj[key] = oldObj[key]
}
}
return newObj;
}
var soldoer2=deepCopy(soldier1)
console.log(soldoer2)
/*输出
{
age:3
equipment:{rifle: "AK-47", pistol: "desertEagle", knife: null}
family:{0: "father", 1: "sister"}
name:"侦察兵"
other:null
}
*/
console.log(soldier1==soldoer2) //输出 false,已经深拷贝
网友评论