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