1.代码执行后弹出:100 50
var age=100;
function test()
{
this.age=50;
return function()
{
return this.age;
}
}
var m=new test();
alert(m());
var n=test();
alert(n());
解析:
var m=new test();
//构造函数改变上下文this变为m;改变了m的age值为50,此时m=function(){return this.age}
alert(m());
//此时上下文环境为window,得出window.age=100;
var n=test();
alert(n());
//等同于alert(test()())
//因为上下文环境一直是window,test()时改变了age的值为50,所以test()()时返回50
new运算符的时候会生成一个对象,叫做空对象。然后调用new运算符后面的函数,也就是类,执行的过程,会把this绑定到空对象上。所以第一个把this是绑定到了生成的空对象上。
所以这个空对象有了age=50的属性。但返回的是函数,不是这个对象。函数中的函数这是闭包,闭包里面的函数的this指向跟是不是闭包没有关系,它依然指向window,于是,你的第一个显示100.
第二个没有生成对象。依然是闭包跟this没有关系,两个函数的调用都指向了window,所以第一次执行 修改为50了,第二次执行显示50。
2.下面代码输出: Goodbye Jack
var name="World!";
(function(){
var name;
if(typeof name=== 'undefined'){
name='Jack';
console.log('Goodbye'+name);
}else{
console.log('hello'+name);
}
})()
解析:
var name="World!";
(function(){
var name; //undefined;
console.log(name==="undefined")//false
console.log(typeof name==="undefined")//ture //这里是隐形转换
//只有 null "" 0 -0 null undefined false 才是false
//而此处的 typeof false 是Boolean类型 所以 隐形转换是ture
if(typeof name=== 'undefined'){
name='Jack'; //进入if
console.log('Goodbye'+name);
}else{
console.log('hello'+name);
}
})()
(function() { … })();是块级作用域,独立于外部执行环境。
局部覆盖全局,name未定义,所以输出Goodbye Jack
3.下面代码输出:10
function test(a){
a=a+10;
}
var a=10;
test(a);
console.log(a);
解析:
a是形参,属于局部变量,不影响全局变量a的值,以不带参数的形式的修改才是全局修改,因此输出的a是全局变量的值10
4.下面代码输出: 1 2 3
var foo = {n:1};
(function(foo){
console.log(foo.n);
foo.n = 3;
var foo = {n:2};
console.log(foo.n);
})(foo);
console.log(foo.n);
解析:
var foo = {n:1};
(function(foo){//形参foo同实参foo一样指向同一片内存空间,这个空间里的n的值为1
var foo; //优先级低于形参,无效。
console.log(foo.n); //输出1
foo.n = 3; //形参与实参foo指向的内存空间里的n的值被改为3
foo = {n:2}; //形参foo指向了新的内存空间,里面n的值为2.
console.log(foo.n); //输出新的内存空间的n的值
})(foo);
console.log(foo.n);//实参foo的指向还是原来的内存空间,里面的n的值为3.
5.x的值是:foo
function A() {
this.do=function() {return ‘foo’;};
}
A.prototype=function() {
this.do=function() {return ‘bar’};
};
var x=new A().do();
A.prototype=function() {
this.do=function() {return ‘bar’};
};
将A的原型重写成了一个函数,如果想要调用到A.prototype中的do,至少是要执行一次A.prototype()的 所以希望在这种定义下打印出"bar",这样写可以实现,但是感觉没什么意义
var x=new A.prototype().do() //"bar"
var x=new A();
console.log(x.__proto__);
//x.__proto__==A.prototype,打印出来的是一个方法,而不再是一个原型对象
console.log(x.__proto__.do()) //报错
其实,不仅仅是说不能再在原型上找到do,就连正常的原型对象都不存在了
function A() {
this.do=function() {return 'foo';};
}
A.prototype=new function() {
this.do=function() {return 'bar'};
};
//使用new,构造函数
var x=new A();
console.log(x.__proto__);
console.log(x.__proto__.do())
6.以下代码执行后, num 的值是? -1
var foo=function(x,y){
return x-y;
}
function foo(x,y){
return x+y;
}
var num = foo(1,2);
解析:
//会被javascript编译器处理为:
//variable hoisting变量提升
var foo;//foo#1
var num;
//function declaration hoisting函数声明提升
function foo(x, y){//foo#2
return x + y;
}
//function expression NOT hoisted函数表达式不会被提升
foo =function(x, y){//foo#3
return x - y;
}
num = foo(1, 2);//这里使用foo#3
规则
4. 变量声明、函数声明都会被提升到作用域顶处;
5. 当出现相同名称时,优先级为:变量声明(foo#1) < 函数声明(foo#2) < 变量赋值(foo#3)
6. 因此,num计算时是用的foo#3。答案为-1。
7.控制台打印的结果是? 0 1 2 2
for(let i=0;i<2;i++){
setTimeout(function(){
console.log(i)
},100
);
}
for(var i=0;i<2;i++){
setTimeout(function(){
console.log(i)
},100
);
}
解析:
第一个:let将i绑定到for循环快中,事实上它将其重新绑定到循环体的每一次迭代中,确保上一次迭代结束的值重新被赋值。setTimeout里面的function()属于一个新的域,通过 var 定义的变量是无法传入到这个函数执行域中的,通过使用 let 来声明块变量,这时候变量就能作用于这个块,所以 function就能使用 i 这个变量了;输出为0,1.
第二个:settimeout是异步执行,1s后往异步任务队列里面添加一个任务,只有同步的全部执行完,才会执行异步任务队列里的任务,当主线执行完成后,i是2,所以此时再去执行任务队列里的任务时,所以输出两次2.
8.在浏览器控制台执行以下代码,输入的结果是: 4400 4401 4399 4400
function test(){
var n = 4399;
function add(){
n++;
console.log(n);
}
return {n:n,add:add}
}
var result = test();
var result2 = test();
result.add();
result.add();
console.log(result.n);
result2.add();
解析:
从内存说起。
本数据类型(String,Number,Boolean,Undefined,Null)进行复制的时候,在栈中创建一个新变量,再将值赋给这个新变量;
引用数据类型(Object,数组,函数)复制的时候,在栈中创建一个新变量,再将引用数据类型的堆地址赋值给这个新变量,也就是说这个地址副本和原地址指向的是同一个数据类型。
题目中,test函数返回的{n:n,add:add}中,n是新创建的变量,add是原函数的引用。
执行两次test函数后会生成 两个 不会互相干预的对象。
执行result.add时,相当于执行test()函数中的add(),而add()中有未声明的变量n,程序会解析add的作用域链(这里形成了闭包),向上寻找一个作用域环境,找到test()的环境中的n。最后执行的部分就很简单了。
所以,result.n所代表的的n与result.add()输出的n不是同一个n,第三个是不会变的4399
9.以下代码,在浏览器中执行的结果是 9999 4400
var A={n:4399};
var B=function(){
this.n=9999
}
var C=function(){
var n=8888
}
B.prototype=A;
C.prototype=A;
var b=new B();
var c=new C();
A.n++;
console.log(b.n);
console.log(c.n);
解析:
new运算的具体执行过程:
1)创建一个空对象
2)把这个空对象的__proto__指向构造函数的prototype
3)把这个空对象赋值给this
4)执行构造函数内的代码,注意此时的this指向新对象,this.n=9999 等价于b.n=9999;
然后访问b.n,存在,直接输出b.n。
再去访问c.n,不存在,通过原型链__proto__向上寻找,c.__proto__指向C.prototype也就是A,所以就是输出A.n
var c = new C();
上面这个语句的实际运行过程是这样的。
var c = function() {
var o = new Object();
//第一个参数改变函数的作用域,即相当于在函数内部设置this = o
C.apply(o, argumens);
return o;
}
这样,由于C()函数中
var n = 8888;
这样只是在函数中创建了一个私有变量,并没有为对象执行任何操作,
因此C的实例中不存在名字为“n”的属性。所以,c.n会访问原型中的属性名为“n”的值。
10.以下代码执行后,a.x 和 b.x 的结果分别是 2 undefined
function A(x){
this.x = x;
}
A.prototype.x = 1;
function B(x){
this.x = x;
}
B.prototype = new A();
var a = new A(2), b = new B(3);
delete b.x;
解析:
var a = new A(2), //a.x首先要在自己的构造函数中查找,没有采取原型上找,这里有this.x = x.所以a.x = 2;
b = new B(3); //B.prototype = new A();形成原型链
delete b.x; //但是delete只能删除自己的x不能删除父级的x.
//b.x通过原型链找到构造函数A里面的this.x=x但是没有赋值,所以undefined