云顶集团官网手机版-云顶集团网站

热门关键词: 云顶集团官网手机版,云顶集团网站
又大概是别的什么来头,须要认识她的两大语法
分类:web前端

Uncaught TypeError: Cannot read property 'foo' of undefined. 是一个我们在 JavaScript 开发中都遇到过的可怕错误。或许是某个 API 返回了意料外的空值,又或许是其它什么原因,这个错误是如此的普遍而广泛以至于我们无法判断。

Javascript 基础

我最近遇到了一个问题,某一环境变量出于某种原因没有被加载,导致各种各样的报错夹杂着这个错误摆在我面前。不论什么原因,放着这个错误不处理都会是灾难性的。所以我们该怎么从源头阻止这个问题发生呢?

语法

让我们一起来找出解决方案。

语法概述

一些代码的示例:

// javascript 代码示例

var x; // 声明一个变量

x = 3; // 给一个变量赋值

foo(x, y); // 调用方法,两个参数 x、y

obj.bar(3); // 调用对象obj的方法,传参数3

if ( x === 0) { // 判断 x 的值是否为0
    x = 123;
}

// 定义名称为 baz 的方法,有两个参数,分别为 a 与 b
function baz(a, b) {
    return a + b;
}

注意等于号的两种不同的用户。

  • 一个单独的等于号(=),用户为变量赋值。
  • 三个连续的等于号(===),用户比较两个值。

工具库

语句和表达式

要理解 JavaScript 的语法,需要认识他的两大语法类别:语句和表达式。

  • 语句就是“做事情”。程序其实就是一系列的语句的集合。
  • 表达式就是通过一定的规则算法产生值。他们通常是参数或赋值的右边部分。
    JavaScript中有两种方式来实现 if-then-else,这可以很好的说明语句和表达式之间的区别。

语句的方式:

// 语句的方式
var x;
if (y >= 0) {
    x = y;
} else {
    x = -y;
}

表达式的方式:

// 表达式的方式
var x = y >= 0 ? y : -y;

表达式的方式可以用户函数的参数,而语句的方式不可以作为参数

// 表达式作为参数
myFunction(y >= 0 ? y : -y);

总之,在 JavaScript 中,表达式可以用在所有需要语句的地方。

如果你已经在项目里用到一些工具库,很有可能库里已经有了预防这个问题发生的函数。lodash 里的 _.get都能确保你安全使用对象。

分号

在 JavaScript 中,分号是可选的。然而,我推荐一直带上她,要不 JavaScript 会猜错语句的结束位置。
分号用户结束语句,而不是结束块。有一种情况你会看到分号出现在块之后:函数表达式作为一个表达式时。如果这样的表达式出现在语句的最后,它后面就会跟上一个分号:

// Pattern: var _ = ___;
var x = 3 + x;
var f = function () { };

如果你已经使用了工具库,那么这看起来已经是最简单的方法了。如果你没有使用工具库,继续读下去吧!

注释

JavaScript 有两种类型的注释,单行注释和多行注释,单行注释由两个斜杠 // 开始;多行注释 /* 开始, */ 结束。

使用 短路

变量和赋值

JavaScript 里的变量在声明后使用:

var foo; // 生命变量

JavaScript 里有一个关于逻辑运算符的有趣事实就是它不总是返回布尔值。根据说明,『 或者 || 运算符的返回值并不一定是布尔值。而是两个操作表达式的其中之一。』

赋值

变量声明和赋值可以同时进行:

var foo = 0;

也可以为一个已有变量进行赋值:

foo = 1;

举个 运算符的例子,如果第一个表达式的布尔值是 false,那么该值就会被返回。否则,第二个表达式的值就会被使用。这说明表达式 0 1 会返回 0,而表达式 2 3 会返回 3。如果多个 表达式连在一起,它们将会返回第一个 false 植或最后一个值。举个例子,1 2 3 null 4 会返回 null,而 1 2 3 会返回 3。

复合赋值运行符

在 JavaScript 中还会有符合赋值预算符,如 +=、-=...

x += 1;
x = x + 1;

那么如何安全的获取嵌套对象内的属性呢?JavaScript 里的逻辑运算符会『短路』。在这个 的例子中,这表示表达式会在到达第一个假值时停下来。

标识符与变量名

标识符是 JavaScript 中各种语法的名称。例如,变量名称就是一个标识符。标识符是区分大小写。

大致来讲,标识符的第一个字符可以是任意的 Unicode 字符、美元符($),或者下划线(_)。后面的字符除此之外还可以为任意 Unicode 数字。因此,以下这些都是合法的标识符:

arg0
_temp
&element
n

以下是标识符为保留字————它们是语法的一部分,不能作为变量名称使用:

abstract    arguments   boolean break   byte
case    catch   char    class*  const
continue    debugger    default delete  do
double  else    enum*   eval    export*
extends*    false   final   finally float
for function    goto    if  implements
import* in  instanceof  int interface
let long    native  new null
package private protected   public  return
short   static  super*  switch  synchronized
this    throw   throws  transient   true
try typeof  var void    volatile
while   with    yield       

以下三个标识符不是保留字,但同样需要视为保留字:

Infinity
NaN
undefined

最后,对一些标准的全局变量名,同样需要避开。虽然它们用作局部变量时不会音像什么,但是还是会使你的代码变得让人困惑。

复制代码在这个例子中,destroyAllHumans 不会被调用,因为 停止了所有在 false 之后的运算 这可以被用于安全地获取嵌套对象的属性。

JavaScript 有很多值,都是我们预期的编程语言的值:布尔值、数字、字符串、数组等。在 JavaScript 中所有的值都有属性。每一个属性都有一个 key 和一个 value。
可以认为,属性就像是一条记录的字段。通过(.)操作符可以读取属性:

value.propKey

例如:字符串‘abc’有length这个属性:

> 'abc'.length
3

点操作符同样可以用于给属性赋值:

> var obj = {};
> obj.foo = 123;
123
> obj.foo
123

我们也可以通过点操作符来调用方法:

> 'hello'.toUpperCase();
'HELLO'

除了简单,这个方法的一个主要优势就是在处理较少嵌套时十分简洁。然而,当访问深层的对象时,它会变得十分冗长。

原始值和对象

JavaScript 中,对值得区分有点自由。

  • 原始值包括布尔值、数字、字符串、null 和 underfined。
  • 其他的值都是对象。
    这两者之间最主要的区别在于他们的比较方式:每个对象都有一个标识符且这等于自己:
> var obj1 = {};
> var obj2 = {};
> obj1 === obj2;
false
> obj1 === obj1;
true

然而所有的原始值,只要实际值相同,则他们就相等:

> var value1 = 123;
> var value2 = 123;
> value1 === value2
true

对象嵌套的越深,它就变得越笨重。

原始值

以下即为所有的原始值

  • 布尔值: true, false
  • 数值: 1, 2, 1.01
  • 字符串: 'abc'
  • 两个空值:undefined、null

原始值具有以下特点:

  1. 按值进行比较:比较实际的值
  2. 不可改变:原始值的属性不可以改变

『或单元』

对象

所有非原始值都是对象。最常见的对象如下:

  • 简单对象,可以通过对象字面量来创建:
{
    firstName: ‘Jane’,
    lastName: 'Doe'
}

上述对象有两个属性,属性 firstName 的值是 Jane,属性 lastName 的值是 Doe。

  • 数组,可以通过数组字面量来创建:
['apple', 'banana', 'cherry']

上面的数组有3个元素,可以通过数字索引来访问它们。

  • 正则表达式,可以通过正则表达式字面量来创建,
/^a+b+$/

对象具有以下特点

  1. 按引用进行比较
    比较身份标识:每个值都有不同的身份标识:
> {} === {}
false
> var obj1 = {};
> var obj2 = obj1;
> obj1 === obj2
true
  1. 默认可变
    对象属性可以很自由地被改变、添加和移除
> var obj = {};
> obj.foo = 123;
> obj.foo
123

Oliver Steele 提出这个方法并且在他发布的博客里探究了更多的细节,『单元第一章:或单元』我会试着在这里给出一个简要的解释。

undefined 和 null

大多数编程语言都会有一些值表示丢失的信息。 JavaScript 有两个类似的空值,undefined 和 null

  • undefined 的意思是没有值。未被初始化的变量即为 undefined:
> var foo;
> foo
undefined

丢失的参数也会是 undefined:

> function f(x) { return x}
> f()
undefined

访问不存在的属性,也会得到 undefined:

> var obj = {};
> obj.foo
undefined
  • null 的意思是没有对象。在用到对象的时候他表示空值。

undefined 和 null 没有属性,甚至连 toString() 这种标准方法都没有

与上面的短路例子类似,这个方法通过检查值是否为假来生效。如果值为假,它会尝试取得空对象的属性。在上面的例子中,favorites.reading 的值是 null,所以从一个空对象上获得books属性。这会返回一个 undefined 结果,所以0会被用于获取空数组中的成员。

检查 undefined 或 null

通常,函数允许通过 undefined 或 null 来表示缺失的值。可以通过显式的检查来做到同样的事情:

if (x === undefined || x === null) {
    ...   
}

也可以利用 undefined 和 null 都可被视为 false 这一事实来处理:

if (!x) {
    ../   
}

false,0, NaN, 和 '' 都可被视为 false。

这个方法相较于 方法的优势是它避免了属性名的重复。在深层嵌套的对象中,这会成为显著的优势。而主要的缺点在于可读性 — 这不是一个普通的模式,所以这或许需要阅读者花一点时间理解它是怎么运作的。

使用 typeof 和 instanceof 对值分类

有两种对值进行分类的操作符:typeof 主要用于原始值,instanceof 用于对象。

typeof 用法如下:

typeof value

它的返回值会是一个表示这个值类型的字符串。如:

> typeof true
'boolean'
> typeof 'abc'
'string'
> typeof {}
'object'
> typeof []
'object'

typeof 会得到的所有结果:

操作数 结果
undefined 'undefined'
null object
布尔值 boolean
数字 number
字符串 string
函数 function
所有其他的常规值 object
引擎创建的值 ....

instanceof 用法形如:

value instanceof Constr

如果 value 是一个通过 Constr 构造器创建的对象,则返回 true。

> var b = new Bar();
> b instanceof Bar
true
> {} instanceof Object
true
> [] instanceof Object
true
> undefined instanceof Object
false
> null instanceof Object
false

不幸的是,在 JavaScript 里,try...catch 声明不是表达式,它们不会像某些语言里那样计算值。这导致不能用一个简洁的 try 声明来作为设置变量的方法。 有一种选择就是在 try...catch 前定义一个 let 变量。

布尔值

原始布尔值类型包含 true 和 false 两个值。以下运算符会产生布尔值。

  • 二元逻辑运算符: &&,||
  • 前置逻辑运算符:!
  • 比较运算符:相等运算符、比较运算符

虽然这很冗长,但这对设置单一变量起作用然而,把它们写在一块就会出问题。

真值与假值

在 JavaScript 中,可以使用任意值来表示布尔值。它们都会被解释成 true 或 false。

以下值会会解释成 false

  • undefined、null
  • 布尔值:false
  • 数字:-0、NaN
  • 字符串: ''

其他所有的值都会被当做 true。被解释为 false 的值可被称为假值,被解释为 true的值可被称为真值。 Boolean() 作为函数调用时,会将被传入的参数转换为一个布尔值。

> Boolean(undefined)
false
> Boolean(0)
false
> Boolean(3)
true
> Boolean({})
true
> Boolean([])
true

如果任意一个获取属性的尝试失败了,这会导致它们全部返回默认值。 一个可选的方法是用一个可复用的工具函数封装 try...catch。

二元逻辑运算符

JavaScript 中的二元逻辑运算符是短路的。因为如果第一个运算符就足以确定结果的话,则不会对第二个运算符做运算。

false && foo()
true || foo()

此外,二元逻辑运算符会返回运算数中的一个————可能是一个布尔值,也可能不是。对真假的检查将会用于确定返回哪一个。

  1. 与(&&)。如果第一个运算数是假值,返回它。否则,返回第二个运算数。
  2. 或(||)。如果第一个运算数是真值,返回它。否则,返回第二个运算数。

通过一个函数包裹获取对象属性的行为,你可以延后『不安全』的代码,并且把它传入 try...catch。

数字

JavaScript 中所有的数字都是浮点数:

> 1 === 1.0
true

也包含一些特殊的数字

这个方法的主要优势在于它十分自然地获取了属性。只要属性被封装在一个函数中,属性就可以被安全访问,同时可以为不存在的路径返回指定的默认值。

NaN ("not a number")

一个错误的数字

> Number('xyz')
NaN

与默认对象合并

infinity

多数情况下也是第一个错误的值:

> 3/0
Infinity
> Math.pow(2, 1024) // number too large
Infinity

Infinity 比任何一个数都要大。同样的,-Infinity 比任何一个数都要小。这使得这两个数字常用来作为默认值,最大值,最小值。

通过将对象与相近结构的『默认』对象合并,我们能确保获取属性的路径是安全的。

运算符

JavaScript 具有如下算术运算符。

  • 加法:num1 + num2
  • 减法: num1 - num2
  • 乘法: num1 * num2
  • 除法: num1 / num2
  • 取模: num1 % num2
  • 增量: num1++, ++num1
  • 减量: num1--, --num1
  • 负数: -num1
  • 转变为数字: +num1
    全局对象 Math 还会以函数的方式提供更多的算术运算。

然而,需要注意并非单个属性,而是整个嵌套对象都会被覆写。

字符串

字符串可以直接通过字符串字面量来创建。这些字面量限定在单引号或双引号之内。反斜杠()用于转义字符及产生一些控制字符。

'abc'
"abc"
‘Did she say "Hello"?’
"Did she sya "Hello"?"
'That's nice!'
"That's nice!"
'Line 1nLiune 2'
'Backlash: \'

可以通过方括号来访问字符串中的单个字符:

> var str = 'atr'
> str[1]
'b'

字符串的 length 属性可以对字符的个数进行计数:

> 'abc'.length
3

像所有的原始值一样,字符串是不可变得;如果要改变一个已有的字符串,必须创建一个新的才行。

不!为了解决这点,我们需要类似地复制每一个嵌套对象。

字符串运算符

字符串可以通过加号(+)进行连接,如果其中一个运算数是字符串的话,另一个运算数将转换为字符串:

> var messageCount = 3;
> 'You have ' + messageCount + ' messages'
'You have 3 messages'

要在多个步骤中连接字符串,可以使用 += 运算符

> var str = '';
> str += 'Mutiple ';
> str += 'pieces ';
> str += 'are concatenated.';
> str
'Mutiple pieces are concatenated.'

好多了!

字符串的方法

字符串有一些常用的方法。

> 'abc'.slice(1)
'bc'
> 'abc'.slice(1,2)
'b'
> 't xyz  '.trim()
'xyz'
> 'mjolnir'.toUpperCase()
'MJOLNIR'
> 'abc'.indexOf('b')
1
> 'abc'.indexOf('d')
-1

这种模式在这类插件或组件中很常见,它们接受一个包含默认值得大型可配置对象。

语句

JavaScript 中的条件和循环语句将在出场。

这种方式的一个额外好处就是通过编写一个默认对象,我们引入了文档来介绍这个对象。不幸的是,按照数据的大小和结构,复制每一个嵌套对象进行合并有可能造成污染。

条件语句

if 语句有一个 then 从句以及一个可选的 else 从句,具体的执行取决于布尔条件:

if (myvar === 0) {
    // then   
}

if (myvar === 0) {
    // then   
} else {
    // else
}

if (myvar === 0) {
    // then   
} else if (myvar === 1) {
    // else-if   
} else if (myvar === 2) {
    // else-if   
} else {
    // else   
}

推荐始终使用大括号。不过如果从句仅有一个单独的语句时可以不必这么做:

if (x < 0) reutrn -x; 

以下是 switch 语句, fruit 的值会决定要执行哪个 case:

switch (fruit) {
    case 'banana':
        // ...
        break;
    case 'apple':
        // ...
        break;
    default:
        // ...   
}

case 之后跟的运算数可以是任意表达式:在 switch 里的参数会通过 === 来进行比较。

未来:可选链式调用

循环语句

for 循环有如下格式:

for ([init]; [codition]; [post_iteration])
    <statement>

初始化会在循环开始前执行。条件会在每次循环迭代之前做检查,如果是 false 则终止循环。后迭代会在每次循环迭代后执行。

for (var i=0; i<arr.lenght; i++) {
    console.log(arr[i]);   
}

while 循环语句在条件成立的时候回持续循环

var i=0;
while (i<arr.length) {
    console.log(arr[i]);
    i++;
}

do-while 循环语句在条件成立时会持续循环。由于条件跟在代码体之后,所以这些代码体至少会执行一次:

do {
    // ...   
} while (conditrion);

有两条语句适合于所有的循环方式:

  • break 可以跳离循环
  • continue 会开始一个新的循环迭代

目前 TC39 提案中有一个功能叫『可选链式调用』。这个新的运算符看起来像这样:

函数

可以通过函数声明的方式来定义哈数:

function add(param1, param2) {
    return param1 + param2;   
}

上面的代码定义了函数 add,它有两个参数:param1 和 param2,返回值是这两个参数的和。可以这样调用这个函数:

> add(6, 1)
7
> add('a', 'b')
'ab'

除此之外,我们还可以通过给变量 add 赋值为函数表达式的方式来定义 add 函数:

var add = function (param1, param2) {
    return param1 + param2;  
};

函数表达式会产生一个值,因此可以将函数作为参数直接传递给另外的函数:

someOtherFunction(function (p1, p2) {...});

码?. 运算符通过短路方式运作:如果 ?. 运算符的左侧计算值为 null 或者 undefined,则整个表达式会返回 undefined 并且右侧不会被计算。 为了有一个自定义的默认值,我们可以使用 || 运算符以应对未定义的情况。

函数声明的提升特性

函数声明基友提升特性————它们的实体会被移动到所在作用于的开始处。这样我们就可以引用后面声明的函数。

function foo() {
    bar();
    function bar (){
        ...
    }   
}

注意, var 声明也具有提升的特性,但通过它们执行的赋值却不具备该特性:

function foo() {
    bar();
    var bar = function() {
        // ...  
    };
}

我们该使用哪一种方法?

特殊的变量 arguments

在 JavaScript 中,函数的所有参数都可以被自由调用,它会通过 arguments 变量来是所有参数可用。 arguments 看起来像个数组,但不具备数组的方法:

> function f() { return arguments}
> var args = f('a', 'b', 'c');
> args.length
3
> args[0]
'a'

答案或许你已经猜到了,正是那句老话『看情况而定』。如果可选链式调用已经被加到语言中并且获得了必要的浏览器支持,这或许是最好的选择。然而,如果你不来自未来,那么你有更多需要考虑的。你在使用工具库吗?你的对象嵌套有多深?你是否需要指定默认值?我们需要根据不同的场景采用不同的方法。

参数太多或太少

我们可以通过以下函数来探知,在 JavaScript 中,函数参数太多或太少是如何处理:

function f(x, y) {
    console.log(x, y);
    return toArray(arguments);   
}

额外的参数会被忽略(arguments 除外):

> f('a', 'b', 'c')
a b
['a', 'b', 'c']

丢失的参数会得到 undefined 这个值:

> f('a')
a undefined
['a']
> f()
undefined undefined
[]

译者:Xcco校对者:hanxiansen, Mirosalva原文地址:Avoiding those dang cannot read property of undefined errors

可选参数

以下是一个给参数附上默认值的通用模式:

function pair(x, y) {
    x = x || 0;
    y = y || 0;
    return [x, y];   
}

|| 运算符会在 x 为真值的时候返回 x,否则,会返回第二个运算数:

> pair()
[0, 0]
> pair(3)
[3, 0]
> pair(3, 5)
[3, 5]

强制参数长度

如果想强制一个参数长度,可以通过 arguments.length 来检查:

function pair(x, y) {
    if (arguments.lenght !== 2) {
        throw new Error('Need exactly 2 arguments');   
    }   
}

将 arguments 转换为数组

arguments 不是数组,它只是类似数组。它有 length 属性,可以通过方括号去访问它的元素。不能移除它的元素,也不能对它调用数组的方法。因此,有时候会需要将它转换成数组:

function toArray(arrayLikeObjecy) {
    return Array.prototype.slice.call(arrayLikeObject);
}

异常捕获

最常见的捕获异常的方法如下:

function getPerson(id) {
    if (id<0) {
        throw new Error('ID must not be negative: ' + id);   
    }   
    return { id: id};
}
function getPersons(ids) {
    var result = [];
    ids.forEach(function (id) {
        try {
            var person = getPerson(id);
            result.push(person);   
        } catch(exception) {
            console.log(exception);   
        }
    });
    return result;   
}

使用 try 语句包裹关键代码,如果 try 语句有异常会被抛出那么 catch 语句就会执行。

严格模式

严格模式激活更多的警告以及使 JavaScript 变得更干净。要切换到严格模式,在 JavaScript 文件或者 script 标签第一行输入:

‘use strict’;

你也可以在每一个函数中激活严格模式:

function functionInStrictMode() {
    'use strict';        
}

变量作用域和闭包

在 JavaScript 中,通过在变量前使用 var 语句声明变量:

> var x;
> var x=3;
> var y=4;
ReferenceError: y is not defined

你可以使用单个 var 语句声明和初始化多个变量:

var x=1, y=2, z=3;

但是我推荐使用单独声明每一个变量。因此,之前的语句重写为:

var x=1;
var y=2;
var z=3;

变量在函数的作用域

一个变量的作用域总是完整的函数(函数代码块)

function foo () {
    var x = -123
    if (x<0){
        var tmp = -x;
        ...
    }
    console.log(tmp); // 123
}

变量的提升特性

所有变量声明都会被提升:声明会被移动到函数的开始处,而赋值则仍然会在原来的位置进行。

function foo() {
    console.log(tmp); // undefined 
    if (false) {
        var tmp=3;    
    }   
}

然而在程序内部,上述函数的执行过程其实是这样的:

function foo(){
    var tmp;
    console.log(tmp);
    if (false) {
        tmp = 3;    
    }     
}

闭包

每一个函数都和它周围的变量保持着连接,哪怕它离开被创建时的作用域也是如此:

function createIncrementor(start) {
    return function(){
        start++;
        return start;   
    }   
}

函数开始被创建,在创建结束后即离开它的上下文环境,但它仍然保持着和 start 的连接:

> var inc = createIncrementor(5);
> inc()
6
> inc()
7
> inc()
8

函数以及它所连接的周围作用域中的变量即为闭包。所以,createIncrementor()的返回值其实就是一个闭包。

IIFE 模式:引入一个新的作用域

有时你会想要引入一个新的作用域,例如,防止一个变量编程全局变量。在 JavaScript 中,不能通过块来做,必须使用函数。不过有一种模式可以将函数当做类似块的方式来使用。这种模式被称作为 IIFE(立即调用函数表达式):

(function () { // open IIFE
   var tmp = ...; // not a global variable
}()); // close IIFE

IIFE 是一个定义之后就被立即调用的函数表达式。在函数内部,会有一个新的作用域,以防止 tmp 变成全局变量。

IIFE 用例:闭包造成的无意共享

闭包会持续地保持与外部变量的连接,而这有时候并不是你想要的:

var result = [];
for (var i=0; i<5; i++) {
    result.push(function(){ return i});   
}
console.log(result[1]()); // 5 not 1
console.log(result[3]()); // 5 not 3

返回值总是 i 的当前值,而并非函数被创建时的值。在循环结束之后,i 的值为5,所以数组中所有的函数都返回这个数值。如果你想要这行函数获得当前 i 值得一个快照,那么你可以使用 IIFE:

for (var i=0; i<5; i++) {
    (function(){
        var i2 = i;
        result.push(function(){return i2});
    }());   
}

对象和构造函数

JavaScript 两种基础的面向对象机制:单一对象和构造函数。

单一对象

和所有值一样,对象也具有属性。你可以认为对象是一组属性的集合,事实也是如此,每个属性都是一个键值对。键名都是字符串,而值可以是 JavaScript 的任意值。

在 JavaScript 中,可以直接通过对象字面量去创建普通对象:

'use strict';
var jane = {
    name: 'Jane',
    describe: function() {
        return 'Person name '+ this.name;  
    }   
}

上述对象具有 name 和 describe 两个属性。你可以获取(get)以及设置(set)这些属性:

> jave.name // get
'Jane'
> jane.name = 'John' // set
> jane.newProperty = 'abc' // property create automatically

以函数作为值的属性被称为方法,如 describe。他们使用 this 对调用他们对象进行引用:

> jane.describe() // call method
'Person name John'
> jane.name = 'Jane'
> jane.describe()
'Person name Jane'

使用 in 运算符检查属性是否存在

> 'newProperty' in jane
true
> 'foo' in jane
false

如果读取一个不存在的属性,会得到 undefined。因此,之前的两个检查可以这样执行:

> jane.newProperty !== undefined
true
> jane.foo !== undefined
false

使用 delete 运算符移除属性

> delete jane.newProperty
true
> 'newProperty' in jane
false

任意属性名

属性的键名可以是任何字符串

> var obj = {'not an identifier': 123};
> obj['not an identifier']
123
> obj['not an identifier'] = 456;

方括号可以用来动态计算属性键名

> var obj = {hello: 'world'};
> var x = 'hello';
> obj[x]
'world'
> obj['hel'+'lo']
'world'

提取方法

如果对方法进去提取,则会失去与对象的连接。就这个函数而言,它不再是一个方法,this 的值也会是 undefined(在严格模式下).

'use strict';
var jane = {
    name: 'Jane',
    describe: function(){
        return 'Person name '+this.name;
    }
}

我们要从 jane 对象中提取 describe 方法,将它赋值给变量 func,然后对它进行调用。你会发现,他不能正常运行:

> var func = jane.describe;
> func()
TypeError: Cannot read property 'name' of undifined

处理这个问题的解决方案可以使用 bind() 方法,所有函数都支持。它会创建一个 this 总是指向给定值得新函数:

> var func2 = jane.describe.bind(jane);
> func2()
'Panson named Jane'

方法中的函数

所有函数都有其特殊的 this 变量。如果在方法中有嵌套函数,这可能会不太方便,因为在嵌套函数内部不能访问方法中的 this 变量。

var jane = {
    name: 'Jane',
    friends: ['Tarzen', 'Cheeta'],
    logHiToFriends: function(){
        'use strict';
        this.friends.forEach(function(friend){
            // this is undefined here
            console.log(this.name+' says hi to '+friend);
        });
    }
}

调用 logHiToFiends 会产生一个错误:

> jane.logHiToFriends()
TypeError: Cannot read property 'name' of undefined

让我们看看这个问题的两种解决方法。

  1. 第一种,我们可以将 this 保存在不同的变量中能够:
logHiTiFriends: function() {
    'use strict';
    var that = this;
    this.friends.forEach(function (friend) {
       console.log(that.name+' says hi to '+ friends); 
    }); 
}
  1. 利用 forEach 的第二个参数,它可以给 this 指定一个值:
logHiToFriends: function(){
    'use strict';
    this.friends.forEach(function(friend){
    console.log(this.name+ ' says hi to '+friend);
    }, this);
}

函数表达式在 JavaScript 中通常被当做函数调用中的参数来使用。在这些函数表达式中引用 this 时要特别小心。

构造函数:对象工厂

// Set up instance data
function Point(x, y) {
    this.x = x;
    this.y = y;   
}
// Methods
Point.prototype.dist = function(){
    return Math.sqrt(this.x*this.x+this.y*this.y);     
};

可以看到构造函数包含两部分。第一部分,Point 函数设置实例数据。第二部分,Point.prototype 属性包含一个带有方法的对象。第一部分里面的实例数据是特定于每一个实例的,而之后的方法数据是对所有实例共享的。

可以通过 new 运算符来使用 Point:

> var p = new Point(3, 4);
> p.x
3
> p.dist()
5

p 是 Point 的一个实例

> p instanceof Point
true

数组

数组是一些有序的元素,可以通过索引从0开始被访问。

数组字面量

数组字面量可以方便地创建数组元素:

> var arr = ['a', 'b', 'c'];

前面的数组有三个元素:字符串 a,b,c。你可以通过所以来访问它们:

> arr[0]
'a'
> arr[0] = 'x'
> arr
['x', 'b', 'c']

length 属性表明数组有多少元素。你可以通过使用它来添加或者删除元素:

> var arr = ['a', 'b'];
> arr.length
2
> arr[arr.length] = 'c';
> arr
['a', 'b', 'c']
> arr.length
3
> arr.length = 1
> arr
['a']

in 操作符也可以在数组中正常使用:

> var arr = ['a', 'b', 'c'];
> 1 in arr // is trher an element at index 1
true
> 5 in arr // is there an element at index 5
false

注意数组是对象,所以可以拥有对象属性

> var arr = [];
> arr.foo = 123;
> arr.foo
123

数组方法

数组拥有许多方法:

> var arr = ['a', 'b', 'c'];
> arr.slice(1, 2) // copy elements
['b']
> arr.slice(1)
['b', 'c']
> arr.push('x') // append an element
4
> arr
['a', 'b', 'c', 'x']
> arr.pop() // remove last element
'x'
> arr
['a', 'b', 'c']
> arr.shift() //remove first element
'a'
> arr
['b', 'c']
> arr.unshift('x') // prepend an element
3
> arr
['x', 'b', 'c']
> arr.indexOf('b') // find the index an element
1
> arr.indexof('y')
-1
> arr.join('-') // all ements in a single string
'x-b-c'
> arr.join('')
'xbc'
> arr.join()
'x,b,c'

遍历数组

forEach 迭代数组并且将当前的元素和元素的 index 扔到一个函数中:

['a', 'b', 'c'].forEach(function(elem, index){
    console.log(index + '. '+ elem) 
});

输出下面的内容:

0. a
1. b
2. c

map 通过应用一个函数映射到现有的数组的每个已经存在的元素创建一个新的数组:

> [1,2,3].map(function(x){return x*x})
[1, 4, 9]

正则表达式

JavaScript 内置的支持正则表达式。它们使用斜线分割:

/^abc$/
/[A-Za-z0-9]+/

test()方法:匹配吗

> /^a+b+$/.test('aabb')
true
> /^a+b+$/.test('aaa')
false

exec()方法:匹配以及捕获分组

> /a(b+)a/.exec('_abbba_aba_')
['abbba', 'bbb']

返回数组包含完整的匹配结果

replace()方法:搜索和替换

> '<a> <bbb>'.replace(/<(.*?)>/g, '[$1]')
'[a] [bbb]'

Math

Math 是一个包含运算功能的对象:

> Math.abs(-1)
1
> Math.pow(3, 2)
9
> Math.max(2, -1, 5)
5
> Math.round(1.9)
2
> Math.PI
3.141592653589793
> Math.cos(Math.PI)
-1

本文由云顶集团官网手机版发布于web前端,转载请注明出处:又大概是别的什么来头,须要认识她的两大语法

上一篇:技术能与任何其他在游戏开发中使用的技术竞争 下一篇:这是VuePress的官方文档,由 VuePress 生成的每个页
猜你喜欢
热门排行
精彩图文