js预解析+作用域+this指向

预解析一

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.

复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值