预解析一
var name = 'World!';
(function() {
if (typeof name === 'undefined') {
var name = 'Jack';
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name);
}
})();
复制代码
答案:Goodbye Jack
注释
1.代码执行前,先预解析。
2.var变量提升,提升到当前作用域最前面。
3.首先给全局的变量name赋值为'World!',然后匿名函数自调用。
4.把匿名函数里的变量name提升到当前作用域最前面。
5.匿名函数里的name声明但未赋值,所以name为undefined。typeof name === undefined为true。
6.所以执行结果为Goodbye Jack。
下面是预解析代码过程
var name;
(function() {
var name; 变量声明未赋值==>undefined
if (typeof name === 'undefined') {
name = 'Jack';
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name);
}
})();
name = 'World!';
复制代码
词法作用域
词法作用域规则:函数的作用域在声明的时候就已经决定了,与调用位置无关。
var a = 10;
function aaa() {
alert(a);
}
function bbb() {
var a = 20;
aaa();
}
bbb();
复制代码
答案:10。
分析:
1.预解析,把a的声明和函数aaa() 和函数bbb() 声明提升到当前作用域最前面。
2.给全局的a赋值为10,然后调用bbb()。
3.函数执行前要预解析,将函数bbb里的变量a提升到当前作用域,然后执行函数bbb。并给a赋值为20.再调用函数aaa()。
4.因为函数aaa的作用域在声明的时候就已经在全局了,所以aaa内部访问变量a的时候,按照作用域链的查找规则,找到全局的a。并alert(a)为10。
下面是预解析代码过程
var a;
function aaa(){
alert(a);
}
function bbb(){
var a;
a = 20;
aaa();
}
a = 10;
bbb();
复制代码
作用域
题目一:
var scope = 'global scope';
function checkScope() {
var scope = 'local scope';
function f() {
return scope;
}
return f;
}
var fn = checkScope();
var a = fn();
console.log(a);
复制代码
答案:local scope
解析:
1.调用checkScope()函数。并将返回值赋值给fn,在checkScope作用域里定义了一个变量scope,还有f函数。并把f函数返回赋值给fn。
fn = function() {
return scope;
}
复制代码
2.调用fn函数,并将返回值赋值给a。由于fn的函数作用域是在checkScope作用域内部,所以在调用函数fn时,访问到的scope是checkScope函数作用域内的scope。
3.最终a的值为local scope
题目二:
var scope = 'global scope';
function checkScope() {
var scope = 'local scope';
function f(scope) {
return scope;
}
return f;
}
var fn = checkScope();
var a = fn(123);
console.log(a)
复制代码
答案:123
解析:
1.调用checkScope函数,并将返回值赋值给fn,在checkScope内部定义了一个scope变量,还有一个f函数。把f函数返回并赋值给fn。
fn = function(scope) {
return scope;
}
复制代码
2.调用函数fn,并将返回值赋值给a,由于fn的函数作用域在checkScope函数内部。所以调用fn函数时,进行参数传递。相当于在scope作用域内部声明var scope = 123。虽然fn函数作用域是在checkScope函数内部。但是fn函数内部就有变量scope,所以访问到的是自身scope。
3.所以a的值为123.
题目三
var scope = 'global scope';
function checkScope() {
var scope = 'local scope';
function f() {
scope = 233;
return scope;
}
return f;
}
var fn = checkScope();
var a = fn();
console.log(a);
复制代码
答案:233
解析:
1.调用函数checkScope,并把返回值赋值给fn。函数checkScope内部定义了一个变量scope,并声明了f函数。将f返回并赋值给fn。
fn = function(){
scope = 233;
return scope;
}
复制代码
2.调用fn函数,并赋值给a。fn函数作用域在checkScope函数内部。所以在调用fn函数时,访问到的scope是checkScope函数作用域内的scope。
3.所以a的值为233.
new关键字
1.创建了一个空对象
2.让this指向了这个空对象
3.执行构造函数里的代码
4.返回this指向的这个对象
题目一:
function A() {
console.log('A');
return 'aaa';
}
var obj1 = new A();
console.log(obj1);
复制代码
答案:
A A{}
复制代码
解析: 如果函数的返回值为基本数据类型(string,number,boolean,null,undefined) 则JS引擎会忽略该返回值,并创建一个新对象。
题目二
function B() {
console.log('B');
return { b: 'bbb' };
}
var obj2 = new B();
console.log(obj2);
复制代码
答案:
B { b: 'bbb' }
复制代码
解析:如果函数的返回值为引用类型,则实际返回值为这个引用数据类型。
题目三
function C() {
console.log('C');
}
var obj3 = new C();
console.log(obj3);
复制代码
答案:
C C{}
复制代码
解析:如果函数没有返回值,则返回this指向的对象。
题目四
function D() {
console.log('D');
return this;
}
var obj4 = new D();
console.log(obj4);
复制代码
答案:
D D{}
复制代码
解析:如果函数返回值是this,那么与没有返回值结果是一样的。
题目五
function obj5() {
console.log('E');
}
new obj5();
复制代码
答案:E
解析:因为没有变量接收这个实例对象。所以new四步操作最后一步不执行。不返回this指向的新对象。所以打印E。
this的指向
1.函数调用模式
2.方法调用模式
3.构造函数调用模式
4.上下文调用模式
复制代码
题目一
var name = 'windowsName';
function a() {
var name = 'Cherry';
console.log(this.name);
console.log(this);
}
a();
复制代码
答案:
windowsName window
复制代码
解析:函数调用模式,this指向window
题目二
var name = 'windowsName';
var a = {
name: 'Cherry',
fn: function() {
console.log(this.name);
}
};
a.fn();
复制代码
答案:Cherry
解析:方法调用模式,this指向的a
题目三
var name = 'windowsName';
var a = {
fn: function() {
console.log(this.name);
}
};
a.fn();
复制代码
答案:undefined 解析: 方法调用模式。this指向的window.a ==> a.name =>undefined
题目三
var name = 'windowsName';
var a = {
name: 'Cherry',
fn: function() {
console.log(name);
console.log(this.name);
}
};
var f = a.fn;
f();
复制代码
答案:windowsName windowsName
解析:函数调用模式。this指向的window。
题目四
var name = 'windowsName';
function fn() {
var name = 'Cherry';
innerFunction();
function innerFunction() {
console.log(this.name);
}
}
fn();
复制代码
答案:windowsName
解析:函数调用模式,this指向的window。
静态成员和实例成员
什么是实例?
实例就是由构造函数创建出来的对象
什么是成员?
成员是属性和方法的统称
什么是实例成员?
由构造函数创建出来的对象能直接访问的属性和方法,包括:对象本身,以及原型中的所有属性和方法。
什么是静态成员?
由构造函数直接访问到的属性和方法。注意是直接访问的属性和方法,间接获取就不是了。
function Person(name) {
this.name = name;
}
Person.age = 18;
Person.run = function() {
console.log('run');
};
Person.prototype.sayHi = function() {
console.log('hi');
};
var p = new Person('xf');
console.log(p.name);
p.sayHi();
// 实例对象不可以访问到构造函数上的成员
console.log(p.age);
// p.run();
console.log(Person.age);
Person.run();
复制代码
答案:
xf hi undefined 18 run
复制代码
题目一
function Foo() {
getName = function() {
console.log(1);
};
console.log(this);
return this;
}
Foo.getName = function() {
console.log(2);
};
Foo.prototype.getName = function() {
console.log(3);
};
var getName = function() {
console.log(4);
};
function getName() {
console.log(5);
}
// 请写出一下输出结果
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
复制代码
答案:
2 4 window 1 1 2 Foo{} 3
复制代码
解析:
先预解析代码如下:
1.变量和函数声明提升。并合写为表达式。
function Foo() {
getName = function() {
console.log(1);
};
console.log(this);
return this;
}
`var getName;
function getName() {
console.log(5);
}`
上面的写下面表达式这种
var getName = function() {
console.log(5);
}
Foo.getName = function() {
console.log(2);
};
Foo.prototype.getName = function() {
console.log(3);
};
getName = function() {
console.log(4);
};
复制代码
这个打印的4会覆盖上面的表达式5
getName = function() {
console.log(4);
};
复制代码
所以最后解析成下面这样
function Foo() {
getName = function() {
console.log(1);
};
console.log(this);
return this;
}
var getName = function() {
console.log(4);
}
Foo.getName = function() {
console.log(2);
};
Foo.prototype.getName = function() {
console.log(3);
};
复制代码
开始打印解析:
Foo.getName() ==> 构造函数.方法 =>因为构造函数直接访问方法和属性是静态成员。所以打印的是
Foo.getName = function() {
console.log(2);
};
复制代码
getName(); =>
var getName = function() {
console.log(4);
}
复制代码
Foo().getName() ==> 先执行Foo()方法 所以会打印window,然后再调用getName() ,打印1,此处Foo()里的
getName = function() {
console.log(1);
};
```会覆盖外面的
复制代码
var getName = function() {
console.log(4); }
所以会再打印一个1
此时代码变成了
function Foo() {
getName = function() {
console.log(1);
};
console.log(this);
return this;
}
var getName = function() {
console.log(4); 改成=>console.log(1);
}
Foo.getName = function() {
console.log(2);
};
Foo.prototype.getName = function() {
console.log(3);
};
getName(); => 打印1
new Foo.getName(); => new (Foo.getName)() ==> 2
new Foo().getName(); ==> (new Foo()).getName(); 调用实例的getName()方法,
沿着原型链查找。根据属性查找原则。打印的是3.
复制代码