2019独角兽企业重金招聘Python工程师标准>>>
1 预解析-Hoisting
■变量提升
变量提升,很简单,就是把变量提升提到函数的top的地方。我么需要说明的是,变量提升 只是提升变量的声明,并不会把赋值也提升上来。
通常,各类文章和JavaScript相关的书籍都声称:“不管是使用var关键字(在全局上下文)还是不使用var关键字(在任何地方),都可以声明一个变量”。请记住,这是错误的概念:
任何时候,变量只能通过使用var关键字才能声明。
上面的赋值语句:
a = 10;
这仅仅是给全局对象创建了一个新属性(但它不是变量)。“不是变量”并不是说它不能被改变,而是指它不符合ECMAScript规范中的变量概念,所以它“不是变量”(它之所以能成为全局对象的属性,完全是因为VO(globalContext) === global,大家还记得这个吧?)。
■函数提升
函数类型 | 提升可否 | 函数名有无 | 局部作用域使用与否 |
函数申明方式 |
YES | 必须有 | YES |
函数表达式方式 |
NO | 可有可无 | YES |
函数构造器方式 |
NO | NO | NO |
◆函数申明方式(函数调用在函数申明之前,依然可以调用)
function myTest(){
foo();
function foo(){
alert("我来自 foo");
}
}
myTest();
◆函数表达式方式(函数调用在函数表达式之前,不能调用.调用后⇒×TypeError foo is not a function)
function myTest(){
foo();
var foo =function foo(){
alert("我来自 foo");
}
}
myTest();
◆函数构造器方式(不使用局部作用域)
var y = "global";
function constructFunction() {
var y = "local";
//Function()构造函数
//不使用局部作用域
return new Function("return y;");
}
function constFunction() {
var y = "local";
//函数直接量
var f = function () {
//使用局部作用域
return y;
};
return f;
}
//显示 global,因为Function()构造函数返回的函数并不使用局部作用域
alert(constructFunction()());
//显示 lobal,因为函数直接量返回的函数并使用局部作用域
alert(constFunction()());
■函数申明和变量申明的关系和影响
function a(x) {
x * 2;
}
var a;
alert(a);
遇到同名的函数声明,VO不会重新定义,所以这时候全局的VO应该是如下这样的:
VO(global) = {
a: 引用了函数声明“a”
}
而执行a的时候,相应地就弹出了函数a的内容了。
2 hasOwnProperty函数
hasOwnProperty是Object.prototype的一个方法,它可是个好东西,他能判断一个对象是否包含自定义属性而不是原型链上的属性,因为hasOwnProperty 是 JavaScript 中唯一一个处理属性但是不查找原型链的函数。
// 修改Object.prototype
Object.prototype.bar = 1;
var foo = {goo: undefined};
foo.bar; // 1
'bar' in foo; // true
foo.hasOwnProperty('bar'); // false
foo.hasOwnProperty('goo'); // true
只有 hasOwnProperty 可以给出正确和期望的结果,这在遍历对象的属性时会很有用。 没有其它方法可以用来排除原型链上的属性,而不是定义在对象自身上的属性。
但有个恶心的地方是:JavaScript 不会保护 hasOwnProperty 被非法占用,因此如果一个对象碰巧存在这个属性,就需要使用外部的 hasOwnProperty 函数来获取正确的结果
var foo = {
hasOwnProperty: function() {
return false;
},
bar: 'Here be dragons'
};
foo.hasOwnProperty('bar'); // 总是返回 false
// 使用{}对象的 hasOwnProperty,并将其上下为设置为foo
{}.hasOwnProperty.call(foo, 'bar'); // true
当检查对象上某个属性是否存在时,hasOwnProperty 是唯一可用的方法。同时在使用 for in loop 遍历对象时,推荐总是使用 hasOwnProperty 方法,这将会避免原型对象扩展带来的干扰,我们来看一下例子:
// 修改 Object.prototype
Object.prototype.bar = 1;
var foo = {moo: 2};
for(var i in foo) {
console.log(i); // 输出两个属性:bar 和 moo
}
我们没办法改变for in语句的行为,所以想过滤结果就只能使用hasOwnProperty 方法,代码如下:
// foo 变量是上例中的
for(var i in foo) {
if (foo.hasOwnProperty(i)) {
console.log(i);
}
}
这个版本的代码是唯一正确的写法。由于我们使用了 hasOwnProperty,所以这次只输出 moo。如果不使用 hasOwnProperty,则这段代码在原生对象原型(比如 Object.prototype)被扩展时可能会出错。
总结:推荐使用 hasOwnProperty,不要对代码运行的环境做任何假设,不要假设原生对象是否已经被扩展了。
3 this
3.1在全局代码中,this始终是全局对象本身,这样就有可能间接的引用到它了。
3.2在通常的函数调用中,this是由激活上下文代码的调用者来提供的,即调用函数的父上下文(parent context )。this取决于调用函数的方式。
以下是经典案例↓
foo() {
alert(.bar);
}
x = {bar: 10};
y = {bar: 20};
x.test = foo;
y.test = foo;
x.test(); y.test();