JavaScript语法(一)

  1. 一、简介
  2. JavaScript实现
  3. 二、基本概念
  4. 变量:ECMAScript的变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据。
  5. 数据类型:
  6. typeof操作符
  7. 由于undefined值是派生自null值,因此规定对他们的相等判断返回true。
  8. 正负无穷大
  9. NaN
  10. 字符串
  11. Object类型
  12. 二进制补码
  13. 逻辑与 逻辑或
  14. with
  15. 函数参数
  16. 三、变量、作用域和内存
  17. instanceof操作符
  18. 执行环境
  19. JavaScript没有块级作用域
  20. 垃圾收集
  21. 四、引用类型
  22. 数组Array
  23. 数组中length为可写参数,因此可以通过修改length来改变数组长度。
  24. 数组最多可以包含 4 294 967 295 个项,这几乎已经能够满足任何编程需求了。
  25. 在判断数据是否是数组格式时,不使用instanceof操作符,因为这会牵扯多个不同的全局执行环境导致不同版本Array。为了解决这个问题Array.isArray()方法来解决。
  26. 数组在调用toString()、valueOf()和toLocaleString()来输出时,会调用数组内每一项的对应toString()、valueOf()和toLocaleString(),并使用逗号分割。
  27. 栈模式
  28. 队列模式
  29. 重排序方法
  30. concat() 函数会基于当前数组创建新的数组。
  31. slice()
  32. splice() 函数的功能异常强大,切有多种用法。
  33. 位置方法
  34. 迭代方法
  35. 归并方法
  36. Date类型
    1. 其他函数
    2. 日期格式化方法
    3. 日期/时间组件方法
  • RegExp类型
    1. ECMAScript3中的问题
    2. RegExp实例属性
    3. RegExp实例方法
    4. RegExp构造函数属性
  • Function类型
    1. 构造函数
    2. 函数声明与函数表达式
    3. 函数内部属性
    4. 函数属性和方法
  • String 类型
    1. 字符方法
    2. 字符串操作方法
    3. 字符串位置方法
    4. trim 方法
    5. 字符串大小写转换方法
    6. 字符串模式匹配
      1. match:接收正则表达式或者 RegExp 对象。返回一个匹配项目数组。
      2. search:同行,但是返回的是第一个匹配项的索引。否则返回-1。
      3. replace:接收两个参数,第一个同上,但是字符串模式并不会转换为正则;第二个可以是字符串或函数。第一个如果是字符串,只会替换第一个字符串,如果需要替换所有则需要正则对象并且指定全局 g;
      4. split
  • localCompare 方法
  • fromCharCode 方法
  • HTML 方法
  • 单体内置对象
    1. Global
    2. Math
  • 五、对象
  • 属性类型
    1. 数据属性
    2. 访问器属性
  • 创建对象
  • 原型模式
    1. 理解原型对象
    2. 原型与 in 操作符
    3. 原型的问题
      1. 重写原型对象后混乱
      2. 引用类型属性
  • 构造函数与原型模式
  • 动态原型模式
  • 寄生构造函数模式
  • 稳妥构造函数模式
  • 继承问题
    1. 原型链的问题
  • 借用构造函数
  • 组合继承
  • 原型式继承
  • 寄生试继承
  • 寄生组合式继承
  • 一、简介

    JavaScript实现

    由三个部分组成:

    核心(ECMAScript)
    文档对象模型(DOM)
    浏览器对象模型(BOM)

    二、基本概念

    变量:ECMAScript的变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据。

    在函数中不实用var来定义变量时,该变量会自定提升为全局变量

    function test() {
    a = 1;
    }
    test();
    console.log(a); // 1

    但是并不推荐该写法。

    数据类型:

    Undefined
    Null
    Boolean
    Number
    String
    Object

    typeof操作符

    用于检测给定变量的数据类型。

    “undefined”:如果这个值未定义
    “boolean”:如果这个值是布尔值
    “string”:如果这个值是字符串
    “number”:如果这个值是数值
    “object”:如果这个值是对象或null> “function”:如果这个值是函数

    从技术角度讲,函数在ECMAScript中是对象。但是由于函数的特殊性,因此在不同浏览器中对函数的返回也不一样。

    由于undefined值是派生自null值,因此规定对他们的相等判断返回true。

    正负无穷大

    +Infinity
    -Infinity
    isFinite() 用于检测

    NaN

    这是一个特殊的数值,用于返回无法返回的特殊情况的替代数据。(如任何数除以非数值会返回NaN,而不会报错)

    设计NaN的计算都会返回NaN。
    NaN与任何值都不相等,包括自己。
    isNaN()函数完成测试工作。该函数的参数如果是对象,则返回的数值则是对象的valueOf、toString。

    字符串

    String类型是由0个或多个16位Unicode字符组成。

    Object类型

    对象中存在的基础属性和方法

    constructor:构造函数
    hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(而不是实例的原型中)是否存在。
    isPrototypeOf(object):用于检查传入的对象是否是当前对象的原型。
    propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用for-in语法来进行枚举。
    toLocaleString:返回对象的字符串表示。与执行环境的地区对应。
    toString:返回对象的字符串表示。
    valueOf:返回对象的字符串、数值或布尔值表示。通常与toString返回相同。

    二进制补码

    用于表示二进制的负数形式。
    步骤:

    1.求这个数值绝对值的二进制码
    2.求二进制反码。
    3.得到的二进制反码+1

    逻辑与 逻辑或

    在进行非布尔值计算时,返回的值不一定是布尔值。
    作为短路逻辑与和短路逻辑或,在比较值不是布尔值时:

    逻辑与会确定第一个值是true的前提下,返回第二个操作数
    逻辑或会确定第一个值是对象则返回第一个值,如果是false,则返回第二个值

    with

    将代码的作用域设置到一个特定的对象中。目的是为了简化多次编写同一个对象的工作。
    下面代码描述了with的大致效果:

    var qs = location.search.substring(1); 
    var hostName = location.hostname;
    var url = location.href;

    with(location){
    var qs = search.substring(1);
    var hostName = hostname;
    var url = href;
    }

    严格模式下不允许使用with语句

    函数参数

    ECMAScript 中的参数在内部是用一个数组来表示的。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数(如果有参数的话)。,在函数体内可以通过 arguments 对象来 访问这个参数数组,从而获取传递给函数的每一个参数。
    这个arguments并不是数组,而是与数组类似(因为他可以使用方括号语法访问它的每一个元素,使用 length 属性来确定传递进来多少个参数。)
    ECMAScript 中的所有参数传递的都是值,不可能通过引用传递参数。

    由于JS的函数使用类似数组来接受参数,而不会单独给参数指定参数签名,因此无法使用重载技术来设立相同名称不同参数类型和个数的函数。

    三、变量、作用域和内存

    instanceof操作符

    typeof在比对基本数据类型时非常有用,但是在比对引用类型时需要instanceof操作符。

    执行环境

    在 Web 浏览器中,全局执行环境被认为是 window 对象
    在移动端中,全局执行环境被认为是 global 对象

    使用try-catch和with语句可以完成延长作用域链功能。

    JavaScript没有块级作用域

    if (a == 1) {
    var color = '0x0f0';
    }

    注意,这里并不会像C一样创建一个作用域。在 JavaScript 中,if 语句中的变量声明会将变量添加到当前的执行环境(在这里是 全局环境)中。这里尤其要注意for语句中。

    for (var i=0; i < 10; i++){ doSomething(i);
    }
    alert(i); //10

    垃圾收集

    JavaScript 具有自动垃圾收集机制,执行环境会负责管理代码执行过程中使用的内存。

    标记清除:当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。而当变量离开环境时,则将其 标记为“离开环境”。而在此之后再被加上标记 的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。
    引用计数:引用计数的含义是跟踪记录每 个值被引用的次数。当这个值的引用次数变成 0 时,则说明没有办法再访问这 个值了,因而就可以将其占用的内存空间回收回来。但是由于循环引用导致的漏洞,后面不实用该方式清空垃圾。

    四、引用类型

    在JavaScript 也可以使用方括号表示法来访问对象的属性。在使用方括号语法时,应该将要访问的属性 以字符串的形式放在方括号中,从功能上看,这两种访问对象属性的方法没有任何区别。但方括号语法的主要优点是可以通过变量 来访问属性。

    alert(person["name"]); //"Nicholas"
    alert(person.name); //"Nicholas"

    var propertyName = "name";
    alert(person[propertyName]); //"Nicholas"

    数组Array

    依旧建议使用Array来创建数组,而不是用字面量方式创建:

    var arrays = Array();
    var arrays = new Array();

    var arrays = [];

    由于部分浏览器不同原因,可能会导致空跳过的字面量数组数据无法控制。

    数组中length为可写参数,因此可以通过修改length来改变数组长度。

    数组最多可以包含 4 294 967 295 个项,这几乎已经能够满足任何编程需求了。

    在判断数据是否是数组格式时,不使用instanceof操作符,因为这会牵扯多个不同的全局执行环境导致不同版本Array。为了解决这个问题Array.isArray()方法来解决。

    支持该函数的浏览器: IE9+、Firefox 4+、Safari 5+、Opera 10.5+和 Chrome,要在尚未实现这个方法中的浏览器中准确检测数组。

    Object.prototype.toString.call(value) == "[object Array]";

    数组在调用toString()valueOf()toLocaleString()来输出时,会调用数组内每一项的对应toString()valueOf()toLocaleString(),并使用逗号分割。

    join()函数可以修改分割方式。

    栈模式

    数组也提供了一种让数组的行为类似于其他数据结构的方法。数组可以表 现得就像栈一样,LIFO(Last-In-First-Out, 后进先出)的数据结构。

    push():将添加项添加至数组的末尾
    pop():弹出数组最后一项数据
    github

    队列模式

    队列数据结构的访问规则是 FIFO(First-In-First-Out, 先进先出)。队列在列表的末端添加项,从列表的前端移除项。

    push():将添加像添加至数组的末尾
    shift():从数组前段获取一项数据
    github

    重排序方法

    sort:升序排列数组项
    reverse:降序排列数组项

    排序函数可添加一个函数为参数,该函数用来完成排序方式。

    function compare(value1, value2) {
    if (value1 < value2) {
    return -1;
    } else if (value1 > value2) {
    return 1;
    } else {
    return 0;
    }
    }
    values.sort(compare);

    concat() 函数会基于当前数组创建新的数组。

    这个方法首先创建当前数组的一个副本,然后将接受到的参数添加至这个副本的末尾,返回一个新构建的数组(在没有参数时只会复制当前数组并返回副本)。

    slice()

    能够基于当前数组中的一或多个项创建一个新数组。接受一或两个参数,即要返回项的起始和结束位置(左闭又开)。
    如果 slice()方法的参数中有一个负数,则用数组长度加上该数来确定相应的位置。

    splice() 函数的功能异常强大,切有多种用法。

    主要用途是向数组的中部插入项。有如下3中方式:

    删除:可以删除任意数量的项,只需指定 2 个参数(要删除的第一项的位置和要删除的项,左闭又开)。
    插入:可以向指定位置插入任意数量的项,只需提供 3 个参数(起始位置、要删除的项数和要插入的项插入的项可以配置多个)。
    替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定 3 个参数(起始位置、要删除的项数和要插入的任意数量的项)

    splice()方法始终都会返回一个数组,该数组中包含从原始数组中删除的项或空数组。

    位置方法

    indexOf():从头部搜索
    lastIndexOf():从尾部搜索

    两个方法都接收 两个参数:要查找的项和(可选的)表示查找起点位置的索引。

    迭代方法

    为数组定义了 5 个迭代方法。每个方法都接收两个参数

    要在每一项上运行的函数
    运行该函数的作用域对象(可选),函数传递的三个参数

    数组项的值
    该项在数组中的位置
    数组对象本身

    函数如下:

    • event():对数组中的每一项运行给定函数,如果该函数对每一项都返回 true,则返回 true。
    • some():对数组中的每一项运行给定函数,如果该函数对任一项返回 true,则返回 true。
    • filter():对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的数组。
    • forEach():对数组中的每一项运行给定函数。这个方法没有返回值。
    • map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。

    eventsome类似逻辑与和逻辑或,他们都用于检测数组项是否满足条件后的结果判定。
    filter类似数组过滤器,返回新的数组。map则是将所有数组项进行自定义处理,并生成新数组。
    最后的forEach如名称,类似for循环数组,没有返回项。

    归并方法

    这两个方法都会迭 12 代数组的所有项,然后构建一个最终返回的值。

    reduce():从数组的第一项开始,逐个遍历到最后
    reduceRight():从数组的最后一项开始,向前遍历到第一项。

    这两个方法都接收两个参数:一个在每一项上调用的函数和(可选的)作为归并基础的初始值。这个函数接收 4 个参数:前一个值、当前值、项的索引和数组对象。这个函数返回的任何值都会作为第一个参数自动传给下一项。

    Date类型

    Date 类型使用自 UTC(Coordinated Universal Time,国际协调时间)1970 年 1 月 1 日午夜(零时)开始经过 的毫秒数来保存日期。
    ECMAScript 提供了两个方法:Date.parse()Date.UTC()

    Date.parse():如果传入方法的字符串不能表示日期,那么它会返回 NaN。
    Date.UTC():返回表示日期的毫秒数,但它与 Date.parse()在构建值时使用不同的信息。参数分别是年份、基于 0 的月份(一月是 0,二月是 1,以此类推)、月中的哪一天 (1 到 31)、小时数(0 到 23)、分钟、秒以及毫秒数。

    Date的构造函数会分别模仿上面的参数构造进行模仿。

    Date.now():返回表示调用这个方法时的日期和时间的毫秒数。

    其他函数

    • toLocaleString():按照与浏览器设置的地区相适应的格式返回日期和时间。
    • toString():返回带有时区信息的日期和 时间,其中时间一般以军用时间(即小时的范围是 0 到 23)表示
    • valueOf():不返回字符串,而是返回日期的毫秒表示。

    日期格式化方法

    toDateString()——以特定于实现的格式显示星期几、月、日和年;
    toTimeString()——以特定于实现的格式显示时、分、秒和时区;
    toLocaleDateString()——以特定于地区的格式显示星期几、月、日和年;
    toLocaleTimeString()——以特定于实现的格式显示时、分、秒;
    toUTCString()——以特定于实现的格式完整的 UTC 日期。

    日期/时间组件方法

    以上都是对整个日期处理的方法,下面则是对时间中的特定值处理的方法。
    UTC 日期指的是在没有时区偏差的情况下(将日期转换为 GMT 时间) 的日期值。

    getTime():返回表示日期的毫秒数;与valueOf()方法返回的值相同
    setTime(毫秒):以毫秒数设置日期,会改变整个日期
    getFullYear():取得4位数的年份(如2007而非仅07)
    getUTCFullYear():返回UTC日期的4位数年份
    setFullYear(年):设置日期的年份。传入的年份值必须是4位数字(如2007而非仅07)
    setUTCFullYear(年):设置UTC日期的年份。传入的年份值必须是4位数字(如2007而非仅07)
    getMonth():返回日期中的月份,其中0表示一月,11表示十二月
    getUTCMonth():返回UTC日期中的月份,其中0表示一月,11表示十二月
    setMonth(月):设置日期的月份。传入的月份值必须大于0,超过11则增加年份
    setUTCMonth(月):设置UTC日期的月份。传入的月份值必须大于0,超过11则增加年份
    getDate():返回日期月份中的天数(1到31)
    getUTCDate():返回UTC日期月份中的天数(1到31)
    setDate(日):设置日期月份中的天数。如果传入的值超过了该月中应有的天数,则增加月份
    setUTCDate(日):设置UTC日期月份中的天数。如果传入的值超过了该月中应有的天数,则增加月份
    getDay()返回日期中星期的星期几(其中0表示星期日,6表示星期六)
    getUTCDay():返回UTC日期中星期的星期几(其中0表示星期日,6表示星期六)
    getHours():返回日期中的小时数(0到23)
    getUTCHours():返回UTC日期中的小时数(0到23)
    setHours(时):设置日期中的小时数。传入的值超过了23则增加月份中的天数
    setUTCHours(时):设置UTC日期中的小时数。传入的值超过了23则增加月份中的天数
    getMinutes():返回日期中的分钟数(0到59)
    getUTCMinutes():返回UTC日期中的分钟数(0到59)
    setMinutes(分):设置日期中的分钟数。传入的值超过59则增加小时数
    setUTCMinutes(分):设置UTC日期中的分钟数。传入的值超过59则增加小时数
    getSeconds():返回日期中的秒数(0到59)
    getUTCSeconds():返回UTC日期中的秒数(0到59)
    setSeconds(秒):设置日期中的秒数。传入的值超过了59会增加分钟数
    setUTCSeconds(秒):设置UTC日期中的秒数。传入的值超过了59会增加分钟数
    getMilliseconds():返回日期中的毫秒数
    getUTCMilliseconds():返回UTC日期中的毫秒数
    setMilliseconds(毫秒):设置日期中的毫秒数
    setUTCMilliseconds(毫秒):设置UTC日期中的毫秒数
    getTimezoneOffset():返回本地时间与UTC时间相差的分钟数。例如,美国东部标准时间返回300。在某地进入夏令时的情况下,这个值会有所变化

    RegExp类型

    通过 RegExp 类型来支持正则表达式。使用Perl语法,可以创建一个正则表达式。
    var expression = / pattern / flags ;

    • pattern:可以是任何简单或复杂的正则表达式,可以包含字符类、限定符、分组、 向前查找以及反向引用。
      模式中使用的所有元字符都必须转义。正则表达式中的元字符包括: 11 ( [ { \ ^ $ | ) ? * + .] } ] )
    • flags:可带有一或多个标志,正则表达式的匹配模式支持下列 3 个标志。v> g:表示全局(global)模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止;

      i:表示不区分大小写(case-insensitive)模式,即在确定匹配项时忽略模式与字符串的大小写;
      m:表示多行(multiline)模式,即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项。

    一个正则表达式就是一个模式 & 3个标志的组合体。

    有两种写法:
    `
    // 写法一
    var pattern1 = /[bc]at/i;

    // 写法2=二
    var pattern2 = new RegExp(“[bc]at”, “i”);
    `
    由于 RegExp 构造 函数的模式参数是字符串,所以在某些情况下要对字符进行双重转义。所有元字符都必须双重转义,那 些已经转义过的字符也是如此,例如\n(字符\在字符串中通常被转义为\,而在正则表达式字符串中就 会变成\\)。

    ECMAScript3中的问题

    var re = null, i;
    for (i=0; i < 10; i++){
    re = /cat/g;
    re.test("catastrophe"); }
    for (i=0; i < 10; i++){
    re = new RegExp("cat", "g");
    re.test("catastrophe");
    }

    第一个循环中,只会创建一个RegExp实例,并且实例属性不会重置,因此在循环中再次调用test()函数会失败。因为第二次调用是从索引为3的字符开始。
    第二个循环中,使用RegExp构造函数每次调用都会创建正则表达式。因此每次迭代都是在一个新的RegExp实例。因此都会返回true。

    这个问题在ECMAScript5中,规定正则表达式的字面量必须像直接调用RegExp构造函数用于,每次都创建新的RegExp实例。

    RegExp实例属性

    通过这些属性可以得到一个正则表达式的各方面信息。

    global:布尔值,表示是否设置了 g 标志。
    ignoreCase:布尔值,表示是否设置了 i 标志。
    lastIndex:整数,表示开始搜索下一个匹配项的字符位置,从 0 算起。
    multiline:布尔值,表示是否设置了 m 标志。
    source:正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回。

    RegExp实例方法

    • exec():专门为捕获组而设计的。
      接受一个参数,即 要应用模式的字符串,然后返回包含第一个匹配项信息的数组;或者在没有匹配项的情况下返回 null。
      返回类型数组中包含两个属性:index 和 input。

      index:表示匹配项在字符串中的位置。
      input:表示应用正则表达式的字符串。
      在数组中:第一项是与整个模式匹配 的字符串,其他项是与模式中的捕获组匹配的字符串(如果模式中没有捕获组,则该数组只包含一项)。

    var text = "mom and dad and baby";
    var pattern = /mom( and dad( and baby)?)?/gi;
    var matches = pattern.exec(text);
    alert(matches.index); //0
    alert(matches.input); // "mom and dad and baby"
    alert(matches[0]); // "mom and dad and baby"
    alert(matches[1]); // " and dad and baby"
    alert(matches[2]); // " and baby"

    在没有使用全局标志时,每次调用exec()时都只会返回第一个匹配项;而使用了全局标志时,则每次调用都只会返回一个匹配项,但是调用后都会继续从字符串中查找新的匹配项。

    • test():知道目标字符串与某个模式是否匹配
      它接受一个字符串参数。在模式与该参数匹配的情况下返回 true;否则,返回 false。
      var text = "000-00-0000";
      var pattern = /\d{3}-\d{2}-\d{4}/;
      if (pattern.test(text)){
      alert("The pattern was matched.");
      }
      egExp 实例继承的 toLocaleString()和 toString()方法都会返回正则表达式的字面量,与创 建正则表达式的方式无关。
      正则表达式的 valueOf()方法返回正则表达式本身。

    RegExp构造函数属性

    这些属性适用于作用域中的所有正则表达式,并且基于所执行的最近一次正则表达式操作而变化。这些属性分别有一个长属性名和一个短属性名 (Opera 是例外,它不支持短属性名)。

    input($_):最近一次要匹配的字符串。Opera未实现此属性
    lastMatch($&):最近一次的匹配项。Opera未实现此属性
    lastParen($+):最近一次匹配的捕获组。Opera未实现此属性
    leftContext($`):input字符串中lastMatch之前的文本
    multiline($*):布尔值,表示是否所有表达式都使用多行模式。IE和Opera未实现此属性
    rightContext($’):Input字符串中lastMatch之后的文本
    9 个用于存储捕获组的构造函数属性:访问这些属性的语法是 RegExp.$1、RegExp.$2…RegExp.$9,分别用于存储第一、第二……第九个匹配的捕获组。

    var text = "this has been a short summer";
    var pattern = /(.)hort/g;
    /*
    * 注意:Opera 不支持 input、lastMatch、lastParen 和 multiline 属性 * Internet Explorer 不支持 multiline 属性
    */
    if (pattern.test(text)){
    alert(RegExp.input); // this has been a short summer
    alert(RegExp.leftContext); // this has been a
    alert(RegExp.rightContext); // summer
    alert(RegExp.lastMatch); // short
    alert(RegExp.lastParen); // s
    alert(RegExp.multiline); // false
    }

    Function类型

    构造函数

    var sum = new Function("num1", "num2", "return num1 + num2"); // 不推荐

    函数声明与函数表达式

    解析器会率先读取函数声明,并使其在执行 9 任何代码之前可用(可以访问);至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真 正被解释执行。

    alert(sum(10,10));
    function sum(num1, num2){
    return num1 + num2;
    }

    以上代码完全可以正常运行。因为在代码开始执行之前,解析器就已经通过一个名为函数声明提升 (function declaration hoisting)的过程,读取并将函数声明添加到执行环境中。

    alert(sum(10,10));
    var sum = function(num1, num2){
    return num1 + num2;
    };

    上面的代码在运行期间产生错误。

    也可以同时使用函数声明和函数表达式,例如 var sum = function sum(){}。 不过,这种语法在 Safari 中会导致错误。

    函数内部属性

    • this
      this 引用的是函数据以执行的环境对象。

    • arguments
      其中arguments内部有一个属性callee,该属性指向拥有arguments对象的函数。
      因此递归的时候可以直接在函数内部使用this.arguments.callee()调用函数本身。

    当函数在严格模式下运行时,访问 arguments.callee 会导致错误。

    • caller
      这是ECMAScript 5规范化了另一个函数对象的属性。这个属性中保存着调用当前函数的函数的引用, 如果是在全局作用域中调用当前函数,它的值为 null。
      function outer(){
      inner();
      }
      function inner(){
      alert(arguments.callee.caller);
      }
      outer();

      严格模式还有一个限制:不能为函数的 caller 属性赋值,否则会导致错误。

    函数属性和方法

    • 属性

      length:表示函数希望接收的命名参数的个数。
      prototype:prototype 是保存它们所有实例方法的真正所在。toString()和 valueOf()等方法实际上都保存在 prototype 名下,只不过是通过各自对象的实例访问。prototype 属性是不可枚举的,因此使用 for-in 无法发现。

    • 方法

      apply/call/bind:都是可以在特定的作用域中调用函数,类似可以修改函数内的this的指向。前者需要两个参数,分别是运行函数作用域和运行函数的参数数组或者arguments对象;而 call 则在传递的函数参数是逐一列举而不是数组或类数组对象。最后该方法会创建一个函数的实例,将this值绑定到指定的函数内部。
      valueOf:同样也返回函数的代码
      toLocalString/toString:始终返回函数的代码,结果因浏览器而异。

      // 运行函数
      function sum(n1, n2) {
      return n1+n2;
      }

      // apply
      function applyTest(n1, n2) {
      return sum.apply(this, [n1, n2]);
      }

      // call
      function callTest(n1, n2) {
      return sum.call(this, n1, n2);
      }```

      上面看似多次依据,但并非真正的使用方式。而是能够扩充函数赖以运行的作用域:
      ```java
      window.color = 'red';
      var o = { color: 'blue' };

      function getColor() {
      return this.color;
      }

      // 运行
      var color = getColor.call(this); // red
      var color = getColor.call(window); // red
      var color = getColor.call(o) // blue

      经过修改的函数会根据作用域的数据而影响,数据不会和函数存在耦合现象。

    String 类型

    字符方法

    用于访问字符串特定字符函数:两个方法都接收一个基于0的字符位置

    • charAt()
      var s = 'abcdefg';
      s.charAt(2); // c
    • charCodeAt()
      var s = 'abcdefg';
      s.charCodeAt(4); // '101'
      这里返回的是字符编码。

    字符串操作方法

    • concat
      用于将1个或多个字符串拼接起来

      var a = 'aa';
      var b = 'bb';
      var c = 'cc';

      var str = a.concat(b, c); // aabbcc
    • slice

    • substr
    • substring
      上面三个函数都具有分割字符串的效果,都接收一至两个参数。第一个参数是截取字符串的第一个字符位置,第二个是可选参数,slice 和 substring 是截取字符串的停止下标,记住是左闭右开;而 substr 则是表示截取字段的长度。

    字符串位置方法

    • indexOf
    • lastIndexOf
      接收第一个参数为查询字符串,第二个可选参数为开始查找的起始位置。 在没有找到字符串位置返回 -1。

    trim 方法

    删除前缀和后缀的空格。

    字符串大小写转换方法

    • toUpperCase:转为大写。
    • toLowerCase:转为小写。
    • toLocaleUpperCase:转为大写,针对特殊地区。
    • toLocaleLowerCase:转为小写,针对特殊地区。

    字符串模式匹配

    match:接收正则表达式或者 RegExp 对象。返回一个匹配项目数组。
    var text = 'cat, bat, sat, fat';
    var pattern = /.at/

    var matches = text.match(pattern); // pattern.exec(text);
    matches.index; // 0
    matches[0]; // cat
    search:同行,但是返回的是第一个匹配项的索引。否则返回-1。
    var text = 'cat, bat, sat, fat';
    text.search(/at/); // 1
    replace:接收两个参数,第一个同上,但是字符串模式并不会转换为正则;第二个可以是字符串或函数。第一个如果是字符串,只会替换第一个字符串,如果需要替换所有则需要正则对象并且指定全局 g;
    var text = 'cat, bat, sat, fat';
    text.replace('at', 'k'); // ck, bat, sat, fat
    text.replace('/at/g', 'k') // ck, bk, sk, fk
    • 如果第二个字符是字符串还可以有如下使用:
      | 字符序列 | 替换文本 |
      | :———: | :————————————————————————————————: |
      | $$ | $ |
      | $& | 匹配整个模式的字符串 |
      | $\’ | 匹配子字符串之前的字符串 |
      | $` | 匹配子字符串之后的字符串 |
      | $n | 匹配第n个捕获组的子字符串,n为0~9。$1表示第一个捕获组的子字符串。 |
      | $nn | 匹配第nn个捕获组的子字符串。nn为01~99。用法同上。 |

    例子:

    var text = 'cat, bat, sat, fat';
    text.replace(/(.at)/g, 'word($1)');// word(cat), word(bat), word(sat), word(fat)
    • 如果第二个字符是函数
      这个函数共有三个参数:模式的匹配项、模式匹配项在字符串的位置和原始字符串。
    split

    可以根据指定的分隔符来分割字符串为子字符串数组,也可以是 RegExp 对象参数。

    var text = 'red, blue, green, yellow';
    text.split(','); // ['red', 'blue', 'green', 'yellow']
    text.split(',', 2); // ['red', 'blue']
    text.split(/[^\,]+/); // ['', ',', ',', ',', '']

    localCompare 方法

    比较两个字符串并返回下面其中一个:

    • 如果字符串在字母表中应该排在字符串参数之前,则返回一个负数。
    • 如果字符串等于字符串参数,则返回0。
    • 如果字符串在字母表中应该排在字符串参数之后,则返回一个正数。

    fromCharCode 方法

    静态方法,接收字符编码,拼接成字符串。

    String.fromCharCode(104, 101, 108, 108, 111); // hello

    HTML 方法

    简单的 HTML 格式化任务的方法。但是尽量不要使用。
    | 方法 |
    |:————————:|
    | anthor(name) |
    | big() |
    | bold() |
    | fixed() |
    | fontcolor(color) |
    | fontsize(size) |
    | italics() |
    | link(url) |
    | small() |
    | strike() |
    | sub() |
    | sup() |

    单体内置对象


    这个对象并不依赖于宿主环境,而是 ECMAScript 。

    Global

    不属于任何对象的属性和方法。所有在全局作用域中定义的函数和对象,都是 Global 对象的属性。

    • encodeURI() / encodeURI()
    • encodeURIComponent() / decodeURIComponent()
      他们可以对URI进行编码,以便发送给浏览器。

    • eval()
      他像是 ECMAScript 的解析器。只接受一个参数,即要执行的 ECMAScript 字符串。

      eval('alert('hi')');
      alera('hi');

      上面的代码就等价于下面的代码。他会将传入的参数当作实际的 ECMAScript 语句来解析,然后把执行结果插入到原位置。

      var msg = 'hello';
      eval('alert(msg)'); // hello
    • 包含的属性
      | 属性 |
      |:———————:|
      | undefined |
      | NaN |
      | infinity |
      | Object |
      | Array |
      | Function |
      | Boolean |
      | String |
      | Number |
      | Date |
      | RegExp |
      | Error |
      | EvalError |
      | RangeError |
      | ReferenceError |
      | SyntaxError |
      | TypeError |
      | URIError |

    • window对象
      一般情况下没有指出如何访问 Global,因此Web浏览器将这个全局对象作为 window 的一部分。

      var color = 'red';
      function sayColor() {
      alert(window.color);
      }
      window.sayColor(); //red

    Math

    • 属性
      | 属性 | 说明 |
      |:——————:|:——————-:|
      | Math.E | e |
      | Math.LN10 | 10的自然对数 |
      | Math.LN2 | 2的自然对数 |
      | Math.LOG2E | 2为底e的对数 |
      | Math.LOG10E | 10为底e的对数 |
      | Math.PI | pi的值 |
      | Math.SQRT1_2 | 1/2的平方根 |
      | Math.SQRT2 | 2的平方根 |

    • min 和 max
      获取一组数据的最大值和最小值。

    • 舍入方法

      Math.ceil():向上舍入
      Math.floor():向下舍入
      Math.round():标准舍入

    • random
      返回介于0和1之间一个随机数,记住是开区间,即不包括0和1.

    五、对象

    创建包括 Object 或者字面量 {}。

    属性类型

    第五版定义了只有内部才能用的特性(attribute),买哦书了属性的各种特性。一般情况下不能直接访问。表示如下:
    [[Enumerable]]

    数据属性

    有以下4个

    • [[Configurable]]:表示是否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。默认为true。
    • [[Enumerable]]:表示能否通过 for-in 循环返回属性。默认为true。
    • [[Writable]]:表示能否修改属性的值。默认true。
    • [[Value]]:包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。默认undefined。

    例子:

    var person = {
    name: 'Yang'
    };

    上面的对象有一个属性name,值为’Yang’。即[[Value]]特性设置为’Yang’。

    修改属性,Object.defineProperty()方法。接收三个参数:

    属性的对象
    属性名称
    描述符对象

    configurable
    enumerable
    writable
    value

    例子

    var person = {};
    Object.defineProperty(person, "name", {
    writable: false,
    value: 'Yang'
    });

    alert(person.name); // 'Yang'
    person.name = 'Li';
    alert(person.name); // 'Li'

    这里的数据是只读的,不可修改。
    var person = {};
    Object.defineProperty(person, "name", {
    configurable: false,
    value: 'Yang'
    });

    alert(person.name); // 'Yang'
    delete person.name;
    alert(person.name); // 'Li'

    这里表示不能从对象中删除属性。一旦把属性定义为不可配置,就不能再把它变回可配置了。就会报错。
    var person = {};
    Object.defineProperty(person, "name", {
    configurable: false,
    value: 'Yang'
    });

    Object.defineProperty(person, "name", {
    configurable: true,
    value: 'Yang'
    });

    上面的例子就是报错。

    访问器属性

    通过 getter 和 setter 函数设置读取访问器属性。访问器属性有以下四个特性:

    • [[Configurable]]:表示是否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。默认为true。
    • [[Enumerable]]:表示能否通过 for-in 循环返回属性。默认为true。
    • [[Get]]:读取属性时调用的函数,默认undefined。
    • [[Set]]:写入属性时调用的函数,默认undefined。

    访问器属性不能直接定义,必须使用Object.defineProperty()来定义。

    例子:

    var book = {
    _year: 2004,
    edition: 1
    };
    Object.defineProperty(book, "year", {
    get: function() {
    return this._year;
    },
    set: function(newValue) {
    if (newValue > 2004) {
    this._year = newValue;
    this.edition += newValue - 2004;
    }
    }
    });
    book.year = 2005; // 2

    _year的下划线用于表示只能通过对象方法访问的属性。这里添加的访问器属year就包含一个 getter 和一个 setter 函数。
    要创建访问器属性,一般使用下面两个非标准函数:defineGetter() 和 ————defineSetter__()。
    var book = {
    _year: 2004,
    edition: 1
    };
    book.__defineGetter__("year", function() {
    return this._year;
    });
    book.__defineSetter__("year", function(newValue) {
    if (newValue > 2004) {
    this._year = newValue;
    this.edition += newValue - 2004;
    }
    });

    上面代码例子与之前效果一样。

    注意:在不支持Object.defineProperty()方法的浏览器中不能修改[[Configurable]]和[[Enumerable]]。

    Object.defineProperties()支持多个属性设置。
    Object.getOwnPropertyDescriptor()获取给定属性的描述符。

    创建对象

    funcion Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function() {
    ...
    };
    }
    var person1 = new Person('Yang', 19, 'Coder');

    创建对象需要以下四个步骤:

    • 1.创建一个新对象;
    • 2.将构造函数的作用域赋值给新对象,即this;
    • 3.执行构造函数代码;
    • 4.返回新对象;

    并且,person1保存着Person的一个不同的实例,constructor。

    person1.constructor == Person // true

    该属性最初用来标识对象类型,但是使用 instanceof 更优。并且这种方式创建的构造函数是定义在 Global 对象汇总。

    原型模式

    创建的每个函数都有一个 prototype 原型属性,是一个指针指向一个对象。这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。

    理解原型对象

    • 无论什么时候,只要创建一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。
    • 所有原型对象都会自动获得一个constructor构造函数属性,这个属性包含一个指向prototype属性所在函数的指针。
    • 当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向原型对象,即[[Prototype]],但是由于这里不是标准,因此其他实现中不一样。但是她们的链接存在于实例与构造函数的原型对象之间。

    github
    上图包括了文字中描述的内容。
    可以通过 isPrototypeOf() 方法确定对象之间是否存在原型关系。

    Person.prototype.isPrototypeOf(person1); // true

    通过 getPrototypeOf() 方法来返回[[Prototype]]的值。
    Object.getPrototypeOf(person1) == Person.prototype; // true

    通过 hasOwnProperty() 方法可以检测一个属性是否存在与实例中(true),还是在原型(false)。
    person1.hasOwnProperty('name'); // true

    原型与 in 操作符

    in 有两种使用:单独使用和在for-in中使用。单独使用时,in操作符会在通过对象能够访问属性时返回true,无论是在原型还是实例中。
    为了区别是否在原型中的属性,使用 hasOwnProperty() 方法判断。

    function hasPrototypeProperty(object, name) {
    return !object.hasOwnProperty(name) && (name in object);
    }

    for-in 循环时,返回的都是可枚举循环(Emunerated)的属性值。
    Object.keys(obj) 可以返回当前实例中所有的可枚举属性的 key 的字符串数组。注意这里的key是键值。这个函数的功能与 getOwnPropertyNames() 相反,他会获取对象中所有的属性。

    原型的问题

    重写原型对象后混乱

    下面的例子:

    function Person() {}
    var person1 = new Person();
    Person.prototype = {
    constructor: Person,
    name: 'Yang',
    age: 18
    };
    person1.age

    github
    注意在重写原型对象后会切断现有原型与任何之前实例的关系。如上图。

    引用类型属性
    function Person() {};
    Person.prototype = {
    constructor: Person,
    name: 'Yang',
    age: 18,
    friends: ['Li', 'Wu']
    };
    val person1 = new Person();
    val person2 = new Person();

    person1.friends.push('Xia');
    person1.friends === person2.friends; // true

    由于共享原型导致引用类型完全共享。

    构造函数与原型模式

    下面这种模式是ECMAScript中认可度最好的构造类型的方法。

    function Person(name, age) {
    this.name = name;
    this.age = age;
    this.friends = ['Li', 'Xia'];
    };
    Person.prototype = {
    constructor: Person,
    sayName: function() {
    return this.name;
    }
    };

    var person1 = new Person('Yang', 17);
    var person2 = new Person('Wu', 20);

    person1.friends.push('Mi');
    person1.friends === person2.friends; // false;

    动态原型模式

    function Person(name, age) {
    this.name = name;
    this.age = age;
    this.friends = ['Li', 'Xia'];
    if (typeof Person.prototype.sayName != 'Function') {
    Person.prototype.sayName = function() {
    return this.name;
    };
    }
    };

    在不存在原型函数的情况下自动添加原型函数,这样就只需要对构造函数进行封装。

    寄生构造函数模式

    function Person(name, age) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function() {
    return this.name;
    };
    return o;
    }

    整个过程类似工厂模式。

    稳妥构造函数模式

    function Person(name, age) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function() {
    return this.name;
    };
    return o;
    }

    继承问题

    ECMAScript通过原型链来作为实现继承的主要方法。
    原型:每个构造函数都有一个原型对象,原型对象都包括一个指向构造函数的指针;实例则有一个指针指向原型对象。
    原型链:原型对象包含一个指针指向另一个原型,层层递进形成了实例与原型的链条。

    function SuperType() {
    this.property = true;
    }
    SuperType.prototype.getSuperValue = function() {
    return this.property;
    }
    function SubType() {
    this.subproperty = false;
    }
    // 继承SubType
    SubType.prototype = new SuperType();

    SubType.prototype.getSubValue = function() {
    return this.subproperty;
    }

    var instance = new SubType();
    instance.getSuperValue(): //true

    完成代码后看一下类图:
    github
    给 SubType 换了一个新原型;就是 SuperType 的实例。新原型不仅具有作为一个 SuperType 的实例所拥有的全部属性和方法,而且内部的指针指向了 SuperType的原型。
    注意:这里的SubType的原型 constructor 属性不是被重写,而是他的原型指向了另一个对象,SuperType的原型,而他的原型的 constructor 属性指向的是 SuperType。

    原型链的问题

    虽然强大,但是在实现继承上依旧存在问题。最主要的就是来自包含引用类型值的原型。即所有对象都共享原型链上的所有对象数据;第二个是在创建子类型时,无法初始化父类的构造函数。

    借用构造函数

    也叫做伪造对象。即在子类型构造函数的内部调用超类型构造函数。

    function SuperType() {
    this.colors = ['red', 'blue', 'green'];
    }
    function SubType() {
    // 继承了 SuperType
    SuperType.call(this);
    }
    var instance1 = new SubType();
    instance1.colors.push('black');
    instance1.colors; // red blue green black
    var instance2 = new SubType();
    instance2.colors; // red blue green

    通过调用call()方法,创建SubType的环境下调用了SuperType的构造函数。这样就初始化了父类构造函数。

    组合继承

    原型式继承

    寄生试继承

    寄生组合式继承