1.立即执行函数表达式是什么?有什么作用
立即执行函数表达式就是
- 声明一个匿名函数
- 马上执行这个匿名函数
典型写法:( function(){alert('匿名函数')} )()
为什么要用一对括号把匿名函数包起来呢?
因为不加括号,写成function(){alert('匿名函数')} ()会报错,原因是function关键字出现在行首,一律解释成语句。因此,JavaScript引擎看到行首是function关键字之后,认为这一段都是函数的定义,不应该以圆括号结尾,所以就报错了。
解决方法就是不要让function出现在行首,因此可以加点东西,有以下写法
(function(){alert('我是匿名函数')} ()) // 用括号把整个表达式包起来
(function(){alert('我是匿名函数')}) () //用括号把函数包起来
!function(){alert('我是匿名函数')}() // 求反,我们不在意值是多少,只想通过语法检查。
+function(){alert('我是匿名函数')}()
-function(){alert('我是匿名函数')}()
~function(){alert('我是匿名函数')}()
void function(){alert('我是匿名函数')}()
new function(){alert('我是匿名函数')}()
那么立即执行函数表达式有什么作用呢?
作用是创建一个独立的作用域。这个作用域里面的变量,外面访问不到,这样可以避免变量污染
2.求n!,用递归来实现
function recursion(n) {
if (n === 1) {
return 1;
}
return n * recursion(n-1);
}
var result = recursion(10);
console.log(result); //以n等于10为列子,结果:3628800
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('男');
调用getInfo('饥人谷', 2, '男')输出:
name: 饥人谷
age: 2
sex: 男
["饥人谷",2,"男"]
name valley
调用getInfo('小谷', 3)输出
name: 小谷
age: 3
sex: undefined
["小谷",3]
name valley
调用getInfo('男')输出:
name: 男
age: undefined
sex: undefined
["男"]
name valley
注:
1.给函数传入参数时是按顺序传入,没有传入参数则为undefined。
2.在函数内部,可以使用arguments对象获取到该函数的所有传入参数
4.写一个函数,返回参数的平方和?
function sumOfSquares() {
var squaresSum= 0
var paraNum = arguments.length
for (var i = 0; i < paraNum; i++) {
squaresSum = squaresSum + arguments[i]*arguments[i]
}
return squaresSum
}
var result = sumOfSquares(2,3,4)
var result2 = sumOfSquares(1,3)
console.log(result) //29
console.log(result2) //10
注:arguments.length代表传入实参的个数
5.如下代码的输出?为什么
console.log(a);
var a = 1;
console.log(b);
输出:
undefined
error(b is not defined)
注:JS中变量声明会被提前,因此上述代码就相当于
var a
console.log(a); //undefined
a = 1;
console.log(b); //未定义,error
6.如下代码的输出?为什么
sayName('world');
sayAge(10);
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
输出:
hello world
error(sayAge is not a function)
注:JS中函数声明和变量声明一样也会被提前,因此上述代码就相当于
function sayName(name){
console.log('hello ', name);
}
var sayAge
sayName('world'); //输出hello world
sayAge(10); //报错,sayAge不是函数类型
sayAge = function(age){
console.log(age);
};
7.如下代码输出什么? 为什么
var x = 10
bar()
function foo() {
console.log(x)
}
function bar(){
var x = 30
foo()
}
输出结果为:10
理由:
1.首先foo()和bar()函数的声明会被提前,因此bar()可以正常执行
2.接着bar()调用了foo(),而foo()输出了个x
3.在foo()函数的内部并未找到x变量,这时候JS会从声明该函数所在的作用域去找, 以此往上(上述代码中也就是最外层的x,即为10)
8.如下代码输出什么? 为什么
var x = 10;
bar()
function bar(){
var x = 30;
function foo(){
console.log(x)
}
foo();
}
输出结果为:30
理由同上,foo()声明所在的作用域的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
理由同题7,fn2()声明所在的作用域的a的值为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
理由同题7,fn2()声明所在的作用域的a的值为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
这题尤其注意,这题和上面的不同的点在于,由于变量提前导致fn2()的执行是在a变量声明之后,赋值之前(a此时只声明了,还没有赋值,因此为undefined),即函数fn3的实际执行是以下的
function fn3(){
function fn2(){
console.log(a)
}
var a
fn2()
a = 4
}
12.如下代码输出什么?为什么
var obj1 = {a:1, b:2};
var obj2 = {a:1, b:2};
console.log(obj1 == obj2);
console.log(obj1 = obj2);
console.log(obj1 == obj2);
输出:
false
{a: 1, b: 2}
true
理由:
对象的相等,当且仅当他们的引用指向内存中的相同区域时才相等,即他们在栈内存中的引用地址相同
obj1和obj2分别是两个不同的对象引用,里面存放的地址是不同的,即他们指向的区域是不同的,即时区域中的内容是一样的也不相等
而把通过obj1=obj2,将obj2中存放的地址赋给了obj1之后,obj1和obj2指向了同一块区域,所以最后两者相等了
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)
console.log(c)
输出:
1
{name: "jirengu", age: 3}
理由:
a作为实参传给了f1的形参n,相当于把a的值赋给了n,然后进行++n,及n的值变成了2,而a的值仍然是1
c作为实参传给了f2的形参obj,c是一个对象,里面存放的是指向它里面的内容所在内存中区域的地址,在f2中对这个地址指向的内存区域里面的age进行了前++,所以改变了这块内存区域中age的实际的值,即age最后得到3
至于将c.age作为实参传给了f1的形参n,仍然是把age作为一个值赋给了n,并没有改变age本身
注:值的传递并不能改变本身,引用的传递才可以
14.写一个深拷贝函数
浅拷贝:对于字符串类型,浅拷贝是对值的复制,对于对象来说,浅拷贝是对对象地址的复制,并没 有开辟新的栈,也就是复制的结果是两个对象指向同一个地址,修改其中一个对象的属性,则另一个对象的属性也会改变。对于浅拷贝,子对象以及子对象下面的对象都是共用的。
浅拷贝实现函数:
var oldObject = {name:"小明",
age:25,
sex:"男",
city:"南京",
wife:{name:"小牛", sex:"女", age:24, city:"北京"},
friends:["小红", "小白", "小黄"]
}
function shallowCopy(oldObj) {
var newObj = {}
for (var i in oldObj) {
if (oldObj.hasOwnProperty(i)) { //hasOwnProperty()函数用于指示一个对象自身(不包括原型链)是否具有指定名称的属性。如果有,返回true,否则返回false
newObj[i] = oldObj[i]
}
}
return newObj
}
var newObject = shallowCopy(oldObject)
console.log(newObject) //newObject里面的内容和oldObject一样
console.log(newObject.wife == oldObject.wife) //true
console.log(newObject.friends == oldObject.friends) //true,两个true说明newObject的子对象并没有开辟新的内存,和oldObject的子对象共用一块堆内存
深拷贝:深拷贝是开辟新的栈,两个对象对应两个不同的地址,即指向堆中不同的空间,修改一个对象的属性,不会改变另一个对象的属性。深拷贝会递归复制子对象及子对象下面的对象,并且新对象和旧对象不是共用一块堆区域。
深拷贝实现函数:
var oldObject = {name:"小明",
age:25,
sex:"男",
city:"南京",
wife:{name:"小牛", sex:"女", age:24, city:"北京"},
friends:["小红", "小白", "小黄"]
}
function deepCopy(oldObj) {
var newObj = {}
for (var key in oldObj) {
if (typeof oldObj[key] === 'object') {
newObj[key] = deepCopy(oldObj[key]) //是对象的递归拷贝
} else {
newObj[key] = oldObj[key] //非对象直接赋值
}
}
return newObj
}
var newObject = deepCopy(oldObject)
console.log(newObject) //newObject里面的内容和oldObject一样
console.log(newObject.wife == oldObject.wife) //false
console.log(newObject.friends == oldObject.friends) //false,两个false说明newObject的子对象开辟新的内存,和oldObject的子对象的堆内存不一样了