学编程,来源栈;先学习,再交钱
当前系列: ES进阶 修改讲义
复习:



在C#中我们都基本上不讲作用域,因为一切都是自然而然的(用语言描述反而有些困难)。但JavaScript的作用域,让人非常头大!


局部变量

如果一个变量在函数体内部(用var)声明,则该变量的作用域为整个函数体,在函数体外不可引用该变量。(另见:let

不同函数内部的同名变量互相独立,互不影响。

这样被声明的变量被称之为:局部变量。

function scope() {
    var sname = '李志博';
    console.log('in function:' + sname);
}
scope();
console.log('out function:' + sname); 
另外,注意不要晕:

猜一猜这段代码的执行结果:

function blockScope() {
    if (true) {
        var i = 986;
    }
    console.log('i=' + i);
}
blockScope();

@想一想@:为什么?有哪里不对劲么?


全局变量“污染”

不在任何函数内定义的变量具有全局作用域,被称之为全局变量。

演示:

如果script只是在HTML页面中使用,全局变量还可以接受(而且比较方便);

但随着JavaScript规模扩大,一个项目可能引用多个(第三方/其他人写的)类库(js文件),各个文件间的名称冲突就越来越难以避免,给开发/维护带来极大的问题!这就被称之为“全局变量污染”。




全局变量的本质

实际上,var定义的:

  • 全局变量是window的属性
  • 顶层函数是window的方法
所有全局变量,本质上都是挂在window对象上的。

演示:

  • var sname='atai' 之后 window.atai
  • function greet(){} 之后 window.greet()


再次混乱

window.alert = function (message){
    console.log(message);
}

@猜一猜@:再次调用alert()会什么情况?

或者你无意中声明了一个window已有的同名函数:

function alert(message) {
    console.log(message);
}
同时一个HTML页面因为.js文件引用,包含了别人的代码,想一想会出现什么情况?

而且一般来说,这些改动页面刷新之后就会“消失”

然而,我发现了一个例外:window.name。演示:name不会因为页面刷新而改变……






词法作用域

观察以下代码:

    var sname = "飞哥";
    function smart() {
        alert(`${sname}最帅`);
    }

    function reallySmart() {
        var sname = '子祥';
        smart();
        //局部变量sname有更高优先级
        console.log(sname);
    }
    reallySmart();
    //假如var声明在后面
    //var sname = "飞哥";

reallySmart()调用smart(),smart()中需要sname但又没有声明sname,怎么办?

  • 使用reallySmart()中定义的'子祥',还是
  • 全局变量"飞哥"

由var声明的变量的作用范围,其作用域由是由其源代码的书写位置,(而不是在哪里执行)决定的。

所谓词法(lexical),指的是:词法作用域根据源代码中声明变量的位置来确定该变量在何处可用。


JavaScript的函数中不仅可以使用全局变量,还可以使用该函数外部的函数所声明的变量:
    //var age = 100;
    function outFunc(sname) {
        var age = 0;
        function innerFunc() {
            alert(age);   //age定义在innerFunc()之外
        }
        innerFunc();
        //var age = 0;
    } 

这样就形成了一个作用域链:JavaScript会沿着这个链条由内向外查找,直到undefined。

@想一想@:如何剪切/粘贴第一个函数,才能显示:子祥最帅?



体会:JavaScript在处理大型项目,进行工程化开发方面先天不足!由此诞生了很多“奇巧淫技”,以及不断进化的ES标准。

但你以为这样就结束了?too young too simple啊!作为一个“先天严重不足,后天各种补丁”的语言,这一切才刚刚开始……



作业

  1. 使用“模拟名称空间”技术,构建一个函数函数yz.fei.get(number);




eval()

setTimeout()中甚至是一个代码源代码的字符串(强烈推荐):
setInterval(console.log(`源栈欢迎你`)`, 100);
这里面实际上是用到了JavaScript的eval()函数:(仅做了解!)
eval('alert(`atai帅呆了`);');    //eval()可以将字符串当做源代码执行



方法

我们前面学习过对象可以存储数据(属性),其实对象中也可以存储函数(在对象中声明的函数通常被称之为方法method)

var wpz = {
    name: '王胖子',
    study: function () {
        alert('好好学习天天向上');
    }
};
wpz.study();    //用对象调用方法

函数提升

复习:变量提升

以命名方式声明的函数也可以提升。演示:略

那么,赋值给变量的匿名函数呢?

演示:

  • 报:getFirst() is not a function的错误
  • 在console中检查getFirst,值为undefined

@想一想@:为什么?

arguments

@想一想@:能不能在sum()函数内部获得未声明的参数(6)?

所以,JavaScript额外赠送一个arguments:

  • 可以获得传入的(不是定义的)所有参数
  • 类似于Array
  • 只能在函数内部使用

这就会方便很多,比如:sum()函数现在就可以接收任意多个的参数:

        function sum() {
            var sum = 0;
            for (var i = 0; i < arguments.length; i++) {
                sum += arguments[i];
            }
            return sum;
        }


但是,仍然很怪,是不是?

通过arguments下标,我们也可以确定参数位置,给中间的参数赋默认值等……



复习:类和对象

之前我们学习过用花括号({})可以生成生成一个匿名 对象。

匿名类对象,就是一个没有类名的对象。


JavaScript号称是一个“面向对象”的语言,还是提供了很多内置类型的。

演示:native code


包装类

之前我们学过的所有基本(primitive)类型,都有对应的类的:String / Number / Boolean / Array(数组),注意他们的首字母大写。

  1. 在类型前面使用关键字/运算符 new
  2. 类型后面添加一对圆括号,
  3. 圆括号中传入一个相应的值
var n = new Number(123);

我们就能得到一个相应的对象。得到的对象一样可以按基本类型变量使用:

new Number(123) -23    //100

但是,他们属于


不同类型

new Number(123)  == 123    //true
new Number(123)  === 123    //false,因为
typeof new Number(123)     //Object

复习:typeof 123 结果是什么?


如果没有new

@猜一猜@:这样的代码结果是什么?

Number(888) + 98    //注意Number()前面没有new

这是因为在JavaScript中没有真正的“类”的定义,类就是函数,函数也可以是类。(后文详述)

所以你可能会觉得有无new都无所谓。但是:

Boolean('false')          //true
new Boolean('false')   //false

找谁说理去?!


以上这些包装函数,知道就行,不建议在开发中使用。







作业

将每天/课的作业用不同的js文件分隔:


  1. 构建函数
    1. has9(number),可以判断number中是否带有数字9;
    2. has8(number),可以判断number中是否带有数字8;
    3. has6(number),可以判断number中是否带有数字6;
    4. 让get986()通过调用has9()/has8()/has6(),能找出number以内有多少个数字包含:9或者8或6。
  2. 将之前“找出素数”的代码封装成一个函数findPrime(max),可以打印出max以内的所有素数。
  3. 自行设计参数,将之前“累加求和”的代码封装成一个函数Sum(),可以计算任意起始位置、任意步长(如:1,3,5……或者0,5,10,15……)的等差数列之和。
  4. 封装一个函数,建立一个函数getMaxNumber(),可以接受任意多各种类型的参数,并找出里面最大的数(忽略数值以外的其他类型)
  5. 封装一个函数swap(arr, i, j),可以交换数组arr里下标 i 和 j 的值
  6. 利用上面的swap()函数,将“冒泡排序”封装成函数bubbleSort()
  7. 封装函数deleteDuplicated()删除一个数组里面重复的元素
  8. 不使用JavaScript内置函数
    1. 将一个字符串顺序颠倒,比如:'hello,yuanzhan' 变成 'nahznauy,olleh'。
    2. 统计出这段文字中有多少个单词:
      There are two ways to create a RegExp object : a literal notation and a constructor. To indicate strings , the parameters to the literal notation do not use quotation marks while the parameters to the constructor function do use quotation-marks. So the following expressions create the same regular expression


学习笔记
源栈学历
今天学习不努力,明天努力找工作

作业

觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 ES进阶 中继续学习:

下一课: 已经是最后一课了……

多快好省!前端后端,线上线下,名师精讲

  • 先学习,后付费;
  • 不满意,不要钱。
  • 编程培训班,我就选源栈

更多了解 加:

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧

公众号:源栈一起帮

二维码