关于一道面试题

 1 var foo,bar1,bar2;
 2 function Foo(){
 3     this.counter={i:0};
 4     this.counter2=0;
 5 }
 6 function Bar(){};
 7 foo=new Foo();
 8 Bar.prototype=foo;
 9 Bar.prototype.sayHello=function(){
10     console.log(this.counter.i,this.counter2);
11     this.counter.i+=1;
12     this.counter2+=1;
13 }
14 var bar1=new Bar();
15 var bar2=new Bar();
16 foo.sayHello();
17 bar1.sayHello();
18 bar2.sayHello();
19 foo.sayHello();
 1 一眼瞟过,大概的意思是:
 2 
 3 声明了3个变量:foo,bar1,bar2; 
 4 
 5 接着声明了2个构造函数:Foo,Bar; 
 6 
 7 接着构造函数Foo的实例对象(new Foo)赋值给foo这个变量,
 8 
 9 接着foo这个变量又赋值给构造函数Bar的原型,即Bar.prototype; 
10 
11 接着在构造函数Bar的原型上添加了一个sayHello的方法;
12 
13 接着构造函数Bar的实例对象赋值给变量bar1
14 
15 接着构造函数Bar的实例对象赋值给变量bar2
16 
17 接着执行foo.sayHello();
18 
19 接着执行bar1.sayHello();
20 
21 接着执行bar2.sayHello();
22 
23 接着执行foo.sayHello();
1 //其中bar.prototype = foo就是对象引用  同时bar也继承了foo的所有属性和方法
2 
3 Bar.prototype.counter2=10;//修改Bar原型上的属性 counter2 =10;原本是 0;
4 
5 console.log(foo.counter2);//Foo的实例对象foo下面的counter2 却跟着同时 变成10了

第一个foo.sayHello()执行的结果:

执行第一个foo.sayHello()的时候,因为console.log(this.counter.i,this.counter2)在 this.counter.i+=1;this.counter2+=1;前面,用的值自然就是初始值this.counter={i:0};this.counter2=0;
第一个foo.sayHello()执行的结果就是 0 0

bar1.sayHello()执行的结果:

执行bar1.sayHello()时,由于已经执行过foo.sayHello(),引用地址中的变成 this.counter={i:1}; this.counter2=1;因此执行结果就是 1,1

bar2.sayHello()执行的结果:

执行bar2.sayHello()时,由于已经执行过foo.sayHello()由于是bar2又是一个实例化对象,由于每次实例化时,构造函数里的基本数据类型也会被初始化(this.counter2=0),复合数据this.counter不会被初始化({i:2});因此执行bar2.sayHello()的结果:2 1

第二个foo.sayHello()执行的结果:

执行第一个foo.sayHello()后,foo实例下的属性已经变成this.counter={i:1} this.counter2=1,经过bar1.sayHello()和bar2.sayHello()的执行,改变了只是复合属性this.counter的i值,而this.counter2是基本数据类型,不会影响到,因此,执行第二个foo.sayHello(),由于this.counter={i:3} this.counter2=1,结果就是 3 1

 ps:

基本类型和引用类型的值及使用有什么区别?

此处主要考察ECMAScript变量包含的两种不同数据类型的值:
- 基本类型值, 指的是简单的数据段
- 引用类型值, 指的是那些可能由多个值构成的对象

解析过程有什么不同?

答:将一个值赋给变量时,解析器必须确定这个值是基本类型值(Undefined,Null,Boolean,String,Number),还是引用类型值。
基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值
引用类型的值是保存在内存中的对象,与其他语言不同,JavaScript不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是操作对象的引用而不是实际的对象,为此,引用类型的值是按引用访问的。注意JS中,Sring不是引用类型

基本类型与引用类型对动态属性的支持的区别有哪些?

答:定义基本类型值和引用类型值的方式是类似的:创建一个变量并为该变量赋值
当这个值保存到变量之后,对不同类型值可以执行的操作则大相径庭。对于引用类型的值,我们可以为其添加属性和方法,也可以改变和删除其属性和方法,例如:
var person = new Object();
person.name = "Jack";
console.log(person.name); // "Jack"
以上代码创建了一个对象并将其保存在了变量person中。
我们不能给基本类型的值添加属性,尽管这样做不会导致任何错误(严格模式下会报错)
var name = "Jack";
name.age = 27;
console.log(name.age);   // undefined
通过上述例子,表明只能给引用类型值动态地添加属性
 1 var fullname="Joh Don";
 2 var o={
 3     fullname:'colin Ihrig ',
 4     prop:{
 5         fullname:'Aureio De Rose ',
 6         getFullname:function(){
 7             return (()=>this.fullname)()
 8         }
 9     }
10 }
11 
12 
13 console.log(o.prop.getFullname())//Aureio De Rose 
14 var test=o.prop.getFullname;
15 console.log(test())//Joh Don
 1 var fullname="Joh Don";
 2 var o={
 3     fullname:'colin Ihrig ',
 4     prop:{
 5         fullname:'Aureio De Rose ',
 6         getFullname:function(){
 7             return (function(){return this.fullname})()
 8         }
 9     }
10 }
11 
12 console.log(o.prop.getFullname())//Joh Don
13 var test=o.prop.getFullname;
14 console.log(test())//Joh Don
 1 var fullname="Joh Don";
 2 var o={
 3     fullname:'colin Ihrig ',
 4     prop:{
 5         fullname:'Aureio De Rose ',
 6         getFullname:function(){
 7             return this.fullname
 8         }
 9     }
10 }
11 
12 console.log(o.prop.getFullname())//Aureio De Rose
13 var test=o.prop.getFullname;
14 console.log(test())//Joh Don
 1 var fullname="Joh Don";
 2 var o={
 3     fullname:'colin Ihrig ',
 4     prop:{
 5         fullname:'Aureio De Rose ',
 6         getFullname:function(){
 7             // console.log(this) // {fullname: "Aureio De Rose ", getFullname: ƒ},也即是prop对象
 8             return this.fullname
 9         }
10     }
11 }
12 
13 console.log(o.prop.getFullname())//是谁在调用getFullname()方法 :o对象下面的prop对象,
14 var test=o.prop.getFullname;//function(){return this.fullname}
15 console.log(test())//Joh Don
 1 for(var i=0;i<5;i++){
 2 
 3     setTimeout(function(){
 4 
 5         console.log(i)
 6 
 7     },1000)
 8 
 9 }
10 
11 //输出5个5
12 
13 // setTimeout是异步执行的,遇到setimeout首先会向任务队列里添加1个个任务,只有主线上的全部任务全部执行完了才会去去执行队列的里的任务,
14 // 所以主线执行完后的是i等于5,再去执行队列任务里的定时任务,实际是执行(循环多少次)个setTimeout里的函数
 1 for(let i=0;i<5;i++){
 2 
 3     setTimeout(function(){
 4 
 5         console.log(i)
 6 
 7     },1000)
 8 
 9 }
10 
11 //输出0 1 2 3 4
12 
13 // setTimeout是异步执行的,遇到setimeout首先会向任务队列里添加这个任务,只有主线上的全部任务全部执行完了才会去去执行队列的里的任务,
14 // 这个是let,就有作用域了,那么作用域怎么看呢? 一次循环就是一个域,在这个域里,每次循环后对应的i值也是不一样,因此最后执行setTimeout里的(循环次数)个函数时就有对应的i值,所以输出0 1 2 3 4
 1 function F(){
 2 
 3     this.conuter={n:0}
 4 
 5     this.number=0
 6 }
 7 
 8 function B(){}
 9 
10 B.prototype=new F();
11 
12 B.prototype.change=function(){
13     console.log(this.conuter.n,this.number)
14     this.conuter.n+=1
15     this.number+=1
16 }
17 
18 console.log(B.prototype.constructor===F)//true  共用一个内存地址 
19 
20 
21 var a=new B()
22 
23 var b=new B()
24 
25 a.change()// 0 0
26 
27 a.change()// 1 1
28 
29 b.change()// 2 0
30 
31 a.change()// 3 2
32 
33 //实例化一次 就会初始化一次,引用类型的conuter是保存在内存中的对象,JavaScript不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间,因此复合属性conuter下面的n是无法被实例初始化的
34 //基本数据类型number是按值访问的,因为可以操作保存在变量中的实际的值,number是会在实例化的时候初始化
1 function a(){
2     console.log('b=>',b)//b=> undefined
3     var b=10;
4     var b=function(){}
5 }
6 a()

词法分析,执行a(),a函数的作用域里,又开始预解析,预解析阶段都不执行代码的,遇到var b=undfined, 又遇到var b 还是赋值undfined,开始执行阶段,从上往下依次,执行,由于预解析var b等于undefined,因此打印出undefined,

1 function a(){
2     console.log('b=>',b)//b=> ƒ b(){}
3     var b=10;
4     function b(){}
5 }
6 a()

依旧先词法分析,执行a(),a函数又是一个作用域,又得预解析,遇到var的function开头,或是参数,就会很敏感,首先,遇到var b赋值undefined,遇到function 开的b函数,由于变量提升,就相当于下面:

1 function a(){
2     var b=function(){}
3     console.log('b=>',b)//b=> ƒ b(){}
4     var b=10;
5 }
6 a()

变量提成是提升到作用域顶部,可以再修改下例子

1 console.log(a)//function a(){}
2 function a(){}
3 <=>
4 var a=function(){}
5 console.log(a)//function(){}
 1 var o={
 2     obj:{
 3         getFullname:function(){
 4             setTimeout(()=>console.log(this),1000)//obj
 5         }
 6     }
 7      
 8 }
 9 
10 o.obj.getFullname()

定时器里的箭头函数this 指向的是上一级的父对象

var fullname="Joh Don";
var o={
    fullname:'colin Ihrig ',
    prop:{
        fullname:'Aureio De Rose ',
        getFullname:function(){
            return ()=>this.fullname
        }
    }
}


console.log(o.prop.getFullname()())//Aureio De Rose 
var test=o.prop.getFullname;
console.log(test()())//Joh Don
原文地址:https://www.cnblogs.com/studyshufei/p/9053012.html