javascript作用域、执行上下文、原型和原型链

一、作用域
js中作用域是指可访问变量,对象,函数的集合,也就是调用它们能生效的代码区块。在js中没有块级作用域,只有全局作用域和函数作用域

1、全局,函数作用域

var a = 10
function f1(){
        var b = c = 20;
        console.log(a); ? ? //10
        console.log(c); ? ? //20
        function f2() {
             console.log(b); //20
        }f2();
}
f1();
console.log(a); ? ? //10
console.log(c); ? ? //20
console.log(b); ? ? //error

var b = c = 20 是指 var b = c; c = 20
在f1函数中c没使用var声明,所以c为全局变量,b为局部变量,绑定在f1函数下,外部访问不到。

2、模仿块级作用域
没有块级作用域,但是有if(),for()等块语句,在块语句内部定义的变量会保留在它们已经存在的作用域内,举个栗子:

if(true) {
        var word = ‘hello‘;
        console.log(word);  //hello
}
console.log(word);      //hello

if()语句存在全局作用域下,所以内部定义的变量存在于全局作用域中,无论在哪都可以访问。

function add(num) {
        if(num > 10) {
                var num = 10;
                console.log(num);   //10
        }
        console.log(num);       //10
}
add(11);
console.log(num);   //Uncaught ReferenceError: num is not defined

此时if()在add函数中,内部定义的变量存在于add函数的作用域中,只有在add函数和块语句中才可以访问到,外部无法访问。

3、使用自执行的匿名函数包裹块语句构建块作用域,也叫私有作用域

function add(num) {
        for(var i = 0; i < num; i++) {
            console.log(i);     //0,1,2,3,4,5,6,7,8,9
        }
        console.log(i);     //10
    }
add(10);

将代码改为

function add(num) {
        (function () {
            for(var i = 0; i < num; i++) {
                     console.log(i);    //0,1,2,3,4,5,6,7,8,9
                }
        })()
    console.log(i);     //Uncaught ReferenceError: i is not defined
}
add(10);

此时变量i只能在for()循环中访问到,在add函数和外部都无法访问,并且在匿名函数中定义的任何变量都会在执行结束时被销毁,所以变量i只能在for()循环中使用。

二、执行上下文
javascript运行的代码环境有三种:

  • 全局代码:代码默认运行的环境,最先会进入到全局环境中
  • 函数代码:在函数的局部环境中运行的代码
  • Eval代码:在Eval()函数中运行的代码
  • 全局上下文:是最外围的一个执行环境,web浏览器中被认为是window对象。在初始化代码时会,先进入全局上下文中
  • 执行上下文:每当一个函数被调用时就会为该函数创建一个执行上下文,每个函数都有自己的执行上下文
function f1() {
        var f1Context = ‘f1 context‘;
        function f2() {
                var f2Context = ‘f2 context‘;
                function f3() {
                        var f3Context = ‘f3 context‘;
                        console.log(f3Context);
                }
                f3();
                console.log(f2Context);
        }
        f2();
        console.log(f1Context);
}
f1();
//结果:
//f3 context
//f2 context
//f1 context

全局上下文:拥有f1()
f1()的执行上下文:有变量f1Context和f2()
f2()的执行上下文:有变量f2Context和f3()
f3()的执行上下文:有变量f3Context

ECS:执行环境栈,可以理解为代码执行的土壤,即代码执行的地方
js是单线程,任务都为同步任务的情况下某一时间只能执行一个任务

执行一段代码首先会进入全局上下文中,并将其压入ECS中栈顶
首先执行f1(),为其创建执行上下文,进入到栈顶位置,全局上下文被往下压
f1()中有f2(),再为f2()创建f2()的执行上下文,f2()进入到栈顶位置,f1()被往下压,
依次,最终全局上下文被压入到栈底,f3()的执行上下文在栈顶
f3()执行完后,ECS就会弹出其执行上下文(内部变量随之被销毁),f3()上下文弹出后,f2()上下文来到栈顶,开始执行f2(),依次,最后ECS中只剩下全局上下文,它等到应用程序退出,例如浏览器关闭时销毁。

function foo(i) {
        if(i  == 3) {
                return;
        }
        foo(i+1);
        console.log(i);
}
foo(0);

ECS栈顶为foo(3)的的上下文,直接return弹出后,栈顶变成foo(2)的上下文,执行foo(2),输出2并弹出,执行foo(1),输出1并弹出,执行foo(0),输出0并弹出,关闭浏览器后全局EC弹出,所以结果为2,1,0。

三、原型和原型链
1、对象(普通对象、函数对象)

  • 所有引用类型(函数,数组,对象)都拥有proto属性(隐式原型)
  • 所有函数拥有prototype属性(显式原型)(仅限函数)
  • 原型对象:拥有prototype属性的对象,在定义函数时就被创建

2、构造函数
//创建构造函数

function Word(words){
        this.words = words;
}
Word.prototype = {
        alert(){
                alert(this.words);
        }
}
//创建实例
var w = new Word("hello world");
w.print = function(){
        console.log(this.words);
        console.log(this);  //Person对象
}
w.print();  //hello world
w.alert();  //hello world

print()方法是w实例本身具有的方法,所以w.print()打印hello world;alert()不属于w实例的方法,属于构造函数的方法,w.alert()也会打印hello world,因为实例继承,构造函数原型上的方法。
实例w的隐式原型指向它构造函数的显式原型,指向的意思是恒等于
w.proto === Word.prototype

当调用某种方法或查找某种属性时,首先会在自身调用和查找,如果自身并没有该属性或方法,则会去它的proto属性中调用查找,也就是它构造函数的prototype中调用查找。所以很好理解实例继承构造函数的方法和属性:
w本身没有alert()方法,所以会去Word()的显式原型Word.prototype中调用alert(),即实例继承构造函数的方法。 ?

3、原型和原型链

Function.prototype.a = "a";
Object.prototype.b = "b";
function Person(){}
console.log(Person);    //function Person()
let p = new Person();
console.log(p);         //Person {} 对象
console.log(p.a);       //undefined
console.log(p.b);       //b

实例p上面并没有a属性,那么会通过proto向上查找,根据:
p.proto == Person.prototype,然后Person.prototype上也没有a属性,Person.prototype仍然是一个对象,上面仍然具有proto属性,根据:
Person.prototype.proto == Function.prototype //false

Person.prototype.proto == Object.prototype //true

Object.prototype.proto == null //再上一级就是null了

此时,Object.prototype.b = "b",所以p.a是undefined,而p.b是"b",
因为没有定义Object.prototype.a,只定义了Function.prototype.a

总结:

1.查找属性,如果本身没有,则会去proto中查找,也就是构造函数的显式原型中查找,如果构造函数中也没有该属性,因为构造函数也是对象,也有proto,那么会去它的显式原型中查找,一直到null,如果没有则返回undefined
2.p.proto.constructor? == function Person(){}
3.p._proto.proto_== Object.prototype
4.p.
proto.proto.proto== Object.prototype.proto == null ????????
5.通过
proto__形成原型链而非protrotype

原文地址:http://blog.51cto.com/9161018/2352227

时间: 2024-10-06 11:22:11

javascript作用域、执行上下文、原型和原型链的相关文章

了解JavaScript的执行上下文

转自http://www.cnblogs.com/yanhaijing/p/3685310.html 什么是执行上下文? 当JavaScript代码运行,执行环境非常重要,有下面几种不同的情况: 全局代码--你的代码首次执行的默认环境. 函数代码--每当进入一个函数内部. Eval代码--eval内部的文本被执行时. 在网上你能读到许多关于作用域(scope)的资源,本文的目的是让事情变得更简单,让我们将术语执行上下文想象为当前被执行代码的环境/作用域.说的够多了,现在让我们看一个包含全局和函数

深入理解JavaScript系列+ 深入理解javascript之执行上下文

http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html http://blog.csdn.net/hi_kevin/article/details/37761919    深入理解javascript之执行上下文(execution context)

深入理解Javascript之执行上下文(Execution Context)

在这篇文章中,将比较深入地阐述下执行上下文 - Javascript中最基础也是最重要的一个概念.相信读完这篇文章后,你就会明白javascript引擎内部在执行代码以前到底做了些什么,为什么某些函数以及变量在没有被声明以前就可以被使用,以及它们的最终的值是怎样被定义的.什么是执行上下文 Javascript中代码的运行环境分为以下三种: 全局级别的代码 - 这个是默认的代码运行环境,一旦代码被载入,引擎最先进入的就是这个环境. 函数级别的代码 - 当执行一个函数时,运行函数体中的代码. Eva

理解Javascript之执行上下文(Execution Context)

1>什么是执行上下文 Javascript中代码的运行环境分为以下三种: 全局级别的代码 - 这个是默认的代码运行环境,一旦代码被载入,引擎最先进入的就是这个环境. 函数级别的代码 - 当执行一个函数时,运行函数体中的代码. Eval的代码 - 在Eval函数内运行的代码. javascript是一个单线程语言,这意味着在浏览器中同时只能做一件事情.当javascript解释器初始执行代码,它首先默认进入全局上下文.每次调用一个函数将会创建一个新的执行上下文. 每次新创建的一个执行上下文会被添加

JavaScript的执行上下文

在JavaScript的运行过程中,经常会遇到一些"奇怪"的行为,不理解为什么JavaScript会这么工作. 这时候可能就需要了解一下JavaScript执行过程中的相关内容了. 执行上下文 在JavaScript中有三种代码运行环境: Global Code JavaScript代码开始运行的默认环境 Function Code 代码进入一个JavaScript函数 Eval Code 使用eval()执行代码 为了表示不同的运行环境,JavaScript中有一个执行上下文(Exe

javascript之执行上下文

执行上下文 一个执行上下文可以抽象成一个简单对象.每个执行上下文有系列的属性(我们可以叫做上下文的状态)来跟踪关联代码的处理. 下面的图是一个上下文的结构: 除了这三个必须的属性(变量对象,this对象和作用域链),一个执行上下文可能有其他的附加状态依赖于实现.

【深入理解javascript】执行上下文

参考原文:执行上下文 1.每一个执行上下文,工作分为三个阶段: 准备阶段–>执行阶段–>调用阶段 准备阶段:代码执行之前,设置数据,相当于初始化. 执行阶段:开始执行每一行代码. 调用阶段:可能没有.如果有函数调用,产生新的执行上下文.函数每被调用一次,都会产生一个新的执行上下文环境. 2.代码段 javascript在执行一个代码段之前,都会进行"准备工作"来生成执行上下文.这个"代码段"其实分三种情况--全局代码,函数体,eval代码. 全局代码是一

JavaScript作用域、上下文环境、函数对象的定义与调用、匿名函数的定义与调用、闭包

提到闭包总给人很高深的感觉,网上的例子也数不胜数.但是我发现相当一部分并不容易理解.根据我的观察,是因为这些例子把标题中提到的概念糅杂在了一起,往往越看越糊涂.所以我希望化整为零,拆成简单例子来解释. 1.先看作用域: JavaScript作用域只有两种--全局作用域和函数内作用域,没有代码块作用域.示例: function loop(){ for(var i=0;i<5;i++){ //doSomething; } alert(i); } loop(); //执行函数结果为5. 尽管变量i已经

javascript之执行上下文堆栈

执行上下文堆栈 有三种类型的ECMAScript代码:全局代码,函数代码和eval代码.代码执行在它的执行上下文里. 有唯一的全局上下文,以及可能有多个函数和eval上下文.每一个函数调用,进入到函数的执行上 下文,执行函数的代码.当调用到 eval 函数, 进入到 eval 执行上下文,执行它的代码. 注意到,一个函数或许产生有限个上下文集合,因为每调用一个函数(甚至自身调用)产生一个 新的上下文,并伴随着新的上下文状态: function foo( bar ){} //调用同一函数 //生成

Javascript学习-执行上下文(Execution Contexts)

一.ECStack 执行上下文是一个堆栈,每次进入function时,则会将该function context压入栈,当从function 退出时候,则会从ECStack弹出该Function context. 二.Global Contexts 在程序初始化时候,则会压入到ECStacks中,例如: ECStack = [ globalContext ]; 三.Function Code 当执行进入Function时,ECStack 将会push 进一个新的Item,需要注意的是,该新的Ite