zoukankan      html  css  js  c++  java
  • 面向对象05


    1. 使用 Function 创建函数与原来的方式创建函数
    * Function 是使用字符串构建函数, 那么就可以在程序运行过程中构建函数
    * 以前的函数必须一开始就写好, 再经过预解析, 一步一步的运行
    * 假定从服务器那里拿到了 "[ 1, 2, 3, 4 ]"
    * 将数组形式的字符串, 转换成数组对象
    * var arr = ( new Function( 'return ' + str + ';' ) )();
    2. 自调用函数
    3. eval 函数
    * 将字符串当做代码指向
    4. 绘制完整的原型链结构
    * 以 Person 为例
    * p -> Person.prototype -> Object.prototype -> null
    * Object 构造函数: o -> Object.prototype -> null
    * Person 是 Function 的实例, 继承自 Function.prototype
    * Object 是 Function 的实例, 继承自 Function.prototype
    * Function 也是 Function 创建出来的( * )
    * Function.prototype 继承自 Object.prototype -> null

    # 变量名提升
    ```
    var num = 123;
    function foo1 () {
    console.log( num );
    var num = 456;
    console.log( num );
    }
    foo1();
    ```

    1. 预解析的过程
    2. 代码的执行过程

    程序在执行过程, 会先将代码读取到内存中检查. 会将所有的声明在此时进行标记. 所谓的标记就是
    让 js 解释器知道有这个名字, 后面在使用名字的时候, 不会出现未定义的错误. 这个标记过程就是提升.

    声明:
    1. 名字的声明, 标识符的声明( 变量名声明 )
    * 名字的声明就是让我们的解释器知道有这个名字
    * 名字没有任何数据与之对应
    2. 函数的声明
    * 函数声明包含两部分
    * 函数声明与函数表达式有区别, 函数声明是单独写在一个结构中, 不存在任何语句, 逻辑判断等结构中
    ```
    function f() {
    function func() {
    } // 声明

    if ( true ) {
    function func2() {} // 函数表达式
    }
    var f = function func3 () {}; // 函数表达式

    this.sayHello = function () {}; // 函数表达式


    var i = 1;
    function func4 () {} // 函数声明
    var j = 2;
    }
    ```
    * 首先函数声明告诉解释器有这个名字存在. 该阶段与名字声明一样
    * 告诉解释器, 这个名字对应的函数体是什么

    ```
    var num = 1;
    function num () {
    alert( num );
    }
    num();
    ```
    分析:
    1. 预解析代码, 提升名字
    * 首先提升名字 num
    * 再提升函数名, 但是名字已经存在 因此只做第二部, 让名字与函数体对应上
    * 结论就是 代码中已经有一个 函数 num 了
    2. 开始执行代码, 第一话从 赋值语句开始执行
    * 给 num 赋值为 1
    * 覆盖了函数
    3. 调用 num, 由于 num 中存储是 数字 1, 因此报错



    ```
    var num = 123;
    function foo1 () {
    console.log( num );
    var num = 456;
    console.log( num );
    }
    foo1();
    ```
    1. 预解析, 提升 num 名字和 foo1 函数
    2. 执行第一句话: num = 123;
    3. 执行函数调用
    * 函数调用进入函数的一瞬间也需要预解析. 解析的是变量名 num
    * 在函数内部是一个独立的空间, 允许使用外部的数据. 但是现在 num 声明同名, 即覆盖外面的
    * 执行第一句 打印 num, 没有数据 undefiend
    * 执行第二句 赋值: num = 456
    * 执行第三局 打印 num, 结果 456


    ```
    if ( ! 'a' in window ) {
    var a = 123;
    }
    console.log( a );
    ```
    1. 预解析, 读取提升 a, 有一个名字 a 存在了
    2. in 运算符: 判断某一个字符串描述的属性名是否在 对象中
    * var o = { name: 'jim' }; 'name' in o , 'age' in o
    * 执行第一个判断: ! 'a' in window
    * 'a' in window 真
    * ! 得到 假
    * if 内部的赋值不进行
    3. 打印结果 a 的值为 undefined


    代码:
    ```
    if ( true ) {
    function f1 () {
    console.log( 'true' );
    }
    } else {
    function f1 () {
    console.log( 'false' );
    }
    }
    f1();
    ```
    1. 预解析: 提升 f1 函数, 只保留最后提升的内容, 所以打印是 false
    2. 执行代码, 第一句话就是有一个 空的 if 结构
    ```
    if ( true ) {

    } else {

    }
    ```
    3. 执行函数调用, 得到 false

    问题:
    function foo() {}
    var foo = function () {};

    1. 上面的语法是声明, 可以提升, 因此在函数定义的上方也可以调用
    2. 下面的语法是函数表达式, 函数名就是 foo, 它会提升. 提升的不是函数体
    3. 函数表达式也是支持名字语法的
    ```
    var foo = function func () {
    };

    func();
    ```
    * 函数有一个属性 name, 表示的是函数名. 只有带有名字的函数定义, 才会有 name 属性值, 否则是 ""
    * 但是, 函数表达式的名字, 只允许在函数内部使用. IE8 可以访问
    * () 可以将数据转换为表达式


    新的浏览器中, 写在 if, while, do-while 结构中的函数, 都会将函数的声明转换成 特殊的函数表达式
    将代码
    ```
    if (...) {
    function foo () { ... }
    }
    ```
    转换成
    ```
    if (...) {
    var foo = function foo () { ... }
    }
    ```


    # 面试题

    1. getDate()
    ```
    getYear()
    getFullYear()
    +new Date()
    ```
    2. new Array() 相当于 []
    * 如果没有参数: 创建一个空数组
    * 如果有一个数字参数: 表示创建一个长度为 指定数字的 数组, 所有元素没有值或全为 0
    * 如果有一个非数字的参数: 就表示用该参数初始化数组
    * 如果数组构造函数的参数是多个数据, 都是对他的初始化
    3. cancat( ... )
    * 该方法会展开参数中的一级数组


    # 词法作用域

    ## 作用域

    域表示的就是 范围, 即 作用范围. 就是一个名字在什么地方可以被使用, 什么时候不能使用.

    ### 块级作用域

    即块级别的作用范围
    ```
    // 在 C , Java 等编程语言中, 下面的语法报错
    {
    var num = 123; // int
    {
    console.log( num ); // => 123
    }
    }
    console.log( num ); // 报错
    ```

    ### 在 js 中采用词法作用域

    所谓的 词法( 代码 )作用域, 就是代码在编写过程中体现出来的作用范围. 代码一旦写好, 不用执行,
    作用范围就已经确定好了. 这个就是所谓词法作用域.

    在 js 中词法作用域规则:
    1. 函数允许访问函数外的数据.
    2. 整个代码结构中只有函数可以限定作用域.
    3. 作用规则首先使用提升规则分析
    4. 如果当前作用规则中有名字了, 就不考虑外面的名字


    例子1:
    ```
    var num = 123;
    function foo() {
    console.log( num );
    }
    foo();
    ```
    例子2:
    ```
    if ( false ) {
    var num = 123;
    }
    console.log( num ); // undefiend
    ```
    例子3:
    ```
    var num = 123;
    function foo() {
    var num = 456;
    function func() {
    console.log( num );
    }
    func();
    }
    foo();
    ```
    练习:
    ```
    var num1 = 123;
    function foo1() {
    var num1 = 456;
    function foo2() {
    num1 = 789;
    function foo3 () {
    console.log( num1 );
    }
    foo3();
    }
    foo2();
    }
    foo1();
    console.log( num1 );
    ```

    ## 作用域链

    可以发现只有函数可以制造作用域结构. 那么只要是代码, 至少有一个作用域, 即全局作用域.
    凡是代码中有函数, 那么这个函数就构成另一个作用域. 如果函数中还有函数, 那么再这个作用域中就
    又可以诞生一个作用域. 那么将这样的所有的作用域列出来, 可以有一个结构: 函数内指向函数外的链式结构.

    例如:
    ```
    function f1() {

    function f2() {

    }
    }
    var num = 456;
    function f3() {
    function f4() {

    }
    }
    ```

    绘制作用域链的步骤:
    1. 看整个全局是一条链, 即顶级链, 记为 0 级链
    2. 看全局作用域中, 有什么成员声明, 就以方格的形式绘制到 0 级练上
    3. 再找函数, 只有函数可以限制作用域, 因此从函数中引入新链, 标记为 1 级链
    4. 然后在每一个 1 级链中再次往复刚才的行为

    变量的访问规则
    1. 首先看变量在第几条链上, 在该链上看是否有变量的定义与赋值, 如果有直接使用
    2. 如果没有到上一级链上找( n - 1 级链 ), 如果有直接用, 停止继续查找.
    3. 如果还没有再次往上刚找... 直到全局链( 0 级 ), 还没有就是 is not defined
    4. 注意, 切记 同级的链不可混合查找


    练习:
    绘制作用域链
    ```
    function f1() {
    var num = 123;
    function f2() {
    console.log( num );
    }
    f2();
    }
    var num = 456;
    f1();
    ```

    如何分析代码
    1. 在分析代码的时候切记从代码的运行进度上来分析, 如果代码给变量赋值了, 一定要标记到图中
    2. 如果代码比较复杂, 可以在图中描述代码的内容, 有事甚至需要将原型图与作用域图合并分析


    ```
    var num = 123;
    function f1() {
    console.log( num );
    }
    function f2() {
    var num = 456;
    f1();
    }
    f2();
    ```

    ```
    var num = 123;
    function f1() {
    console.log( num );
    }
    function f2() {
    num = 456;
    f1();
    }
    f2();
    ```


    # 补充
    1. 声明变量使用 var, 如果不使用 var 声明的变量就是全局变量( 禁用 )
    2. 因为在任何代码结构中都可以使用该语法. 那么再代码维护的时候会有问题. 所以除非特殊原因不要这么用.
    3. 下面的代码的错误
    ```
    function foo () {
    var i1 = 1 // 局部
    i2 = 2, // 全局
    i3 = 3; // 全局

    }
    ```
    4. 此时注意
    ```
    var arr = [];
    for ( var i = 0; i < 10; i++ ) {
    arr.push( i );
    }
    for ( var i = 0; i < 10; i++ ) {
    console.log( arr[ i ] );
    }
    // 一般都是将变量的声明全部放到开始的位置, 避免出现因为提升而造成的错误
    var arr = [],
    i = 0;
    for ( ; i < 10; i++ ) {
    arr.push( i );
    }
    for ( i = 0; i < 10; i++ ) {
    console.log( arr[ i ] );
    }
    ```

    # 闭包

    闭包的含义就是闭合, 包起来. 简单的来说就是 一个具有封闭功能与 包裹功能的一个结构. 所谓的闭包就是,
    有一个具有封闭的对外不公开的, 包裹结构, 或空间.

    在 js 中函数可以构成闭包. 一般函数是一个代码结构的封闭结构, 即包裹的特性, 同时根据作用域规则,
    只允许函数访问外部的数据, 外部无法访问函数内部的数据, 即封闭的对外不公开的特性. 因此说函数可以构成闭包.

    ## 闭包要解决什么问题

    1. 闭包不允许外界访问
    2. 要解决的问题就是间接访问该数据

    函数就可以构成闭包, 要解决的问题就是访问到函数内部的数据

    ```
    function foo () {
    var num = 123;
    return num;
    }
    var res = foo();
    console.log( res ); // => 123
    ```
    1. 这里的确是访问到函数中的数据
    2. 但是该数据不能第二次访问. 因为第二次访问的时候又要调用一次 foo, 表示又有一个新的 num = 123 出来了

    在函数内的数据, 不能直接在函数外被访问, 那么再函数内如果定义一个函数, 那么再这个内部函数中是可以直接访问的
    ```
    function foo() {
    var num = Math.random();
    function func() {
    return num;
    }
    return func;
    }
    var f = foo();
    // f 可以直接访问这个 num
    var res1 = f();
    var res2 = f();
    ```

    练习:
    ```
    function foo () {
    var o = { name: 'jim' };
    return function () {
    return o;
    }
    }
    ```

    # 答疑
    函数科里化( 高阶函数 )

    定义一个函数, 该函数返回一个函数, 那么在调用的时候

    ```
    function foo() {
    function func() {
    }
    return func;
    }
    foo()()
    ```

    JavaScript 模式

    ```
    function color( r, g, b ) {
    // ...
    }
    =>
    color( 255, 0, 0 )
    function color ( r ) {
    return function color( g ) {
    return color( b ) {
    }
    }
    }
    color( 255 )( 0 )( 0 )
    ```


    # 闭包

    ## 如何获得超过一个数据

    ```
    function foo () {
    var num1 = Math.random();
    var num2 = Math.random();

    return {
    num1: function () {
    return num1;
    },
    num2: function () {
    return num2;
    }
    }
    }
    ```

    ## 如何完成读取一个数据和修改这个数据

    ```
    function foo () {
    var num = Math.random();
    return {
    get_num: function () {
    return num;
    },
    set_num: function ( value ) {
    num = value;
    }
    }
    }

    ```

    ## 基本的闭包结构

    一般闭包的问题就是要想办法间接的获得函数内数据的使用权. 那么我们的可以总结出一个基本的使用模型.
    1. 写一个函数, 函数内定义一个新函数, 返回新函数, 用新函数获得函数内的数据
    2. 写一个函数, 函数内定义一个对象, 对象中绑定多个函数( 方法 ), 返回对象, 利用对象的方法访问函数内的数据


    ## 闭包的基本用法

    闭包是为了实现 具有私有访问空间的 函数的

    1. 带有私有访问数据的对象
    ```
    function Person() {
    this.name = '张三';
    // setName( '' )
    }
    // 所谓的私有数据, 就是说只有函数内部可以访问的数据, 或对象内部的方法可以访问的数据

    // 1 最简单的实现方式
    function createPerson() {
    var __name__ = "";
    return {
    get_Name: function () {
    return __name__;
    },
    set_Name: function ( value ) {
    // 如果不姓张就报错
    if ( value.charAt( 0 ) === '张' ) {
    __name__ = value;
    } else {
    throw new Error( '姓氏不对, 不能取名' );
    }
    }
    };
    }
    // 2

    ```
    2. 带有私有数据的函数
    ```
    var func = function () {}
    function func () {}

    var foo = (function () {
    // 私有数据
    return function () {
    // 可以使用私有的数据

    };
    })();
    ```

    # 闭包的性能问题?

    函数执行需要内存, 那么函数中定义的变量, 会在函数执行结束后自动回收. 凡是因为闭包结构, 被引出的数据.
    如果还有变量引用这些数据的话, 那么这些数据就不会被回收.

    因此在使用闭包的时候如果不使用某些数据了, 一定要赋值一个 null
    ```
    var f = (function () {
    var num = 123;
    return function () {
    return num;
    };
    })();

    // f 引用着函数, 函数引用变量 num
    // 因此在不使用该数据的时候, 最好写上
    f = null;

  • 相关阅读:
    数据结构 --- 循环队列(队列的顺序存储结构)
    SPOJ MYQ10 10649. Mirror Number 数位DP
    CodeForces 215E Periodical Numbers 数位DP
    CodeForces 234Div2
    URAL 1057 Amount of Degrees 数位DP
    CodeForces 55D Beautiful numbers 数位DP+数学
    HDU 4352 XHXJ's LIS 数位DP + 状压
    SPOJ BALNUM Balanced Numbers 状压+数位DP
    HDU 4909 String 统计+状压
    CodeForces 258B Little Elephant and Elections 数位DP
  • 原文地址:https://www.cnblogs.com/huqinhan/p/5743087.html
Copyright © 2011-2022 走看看