es6语法需要注意的部分

es运算符:

1.无符号右移运算,如果是负数的话则结果就不正确

由于无符号右移运算的结果是一个 32 位的正数,所以负数的无符号右移运算得到的总是一个非常大的数字。例如,如果把 -64 右移 5 位,将得到 134217726。如何得到这种结果的呢?

要实现这一点,需要把这个数字转换成无符号的等价形式(尽管该数字本身还是有符号的),可以通过以下代码获得这种形式:

var iUnsigned64 = -64 >>> 0;

然后,用 Number 类型的 toString() 获取它的真正的位表示,采用的基为 2:

alert(iUnsigned64.toString(2));

这将生成 11111111111111111111111111000000,即有符号整数 -64 的二进制补码表示,不过它等于无符号整数 4294967232。

出于这种原因,使用无符号右移运算符要小心。

 

2.逻辑NOT运算符(!)

  • 如果运算数是对象,返回 false
  • 如果运算数是数字 0,返回 true
  • 如果运算数是 0 以外的任何数字,返回 false
  • 如果运算数是 null,返回 true
  • 如果运算数是 NaN,返回 true
  • 如果运算数是 undefined,发生错误

判断ECMAScript 变量的boolean值时,可以使用两次not运算符

第一个not返回运算符的boolean值,第二个not对该boolean值求负得到真正的变量值

 

3.逻辑and(&&)

如果某个运算数不是原始的 Boolean 型值,逻辑 AND 运算并不一定返回 Boolean 值:

  • 如果一个运算数是对象,另一个是 Boolean 值,返回该对象。
  • 如果两个运算数都是对象,返回第二个对象。
  • 如果某个运算数是 null,返回 null。
  • 如果某个运算数是 NaN,返回 NaN。
  • 如果某个运算数是 undefined,发生错误。

 

4.逻辑或(||)

逻辑 OR 运算并不一定返回 Boolean 值:

  • 如果一个运算数是对象,并且该对象左边的运算数值均为 false,则返回该对象。
  • 如果两个运算数都是对象,返回第一个对象。
  • 如果最后一个运算数是 null,并且其他运算数值均为 false,则返回 null。
  • 如果最后一个运算数是 NaN,并且其他运算数值均为 false,则返回 NaN。
  • 如果某个运算数是 undefined,发生错误。

 

5.关系运算符

如果比较一个数字和字符串,es6会把字符串转化为数字然后按照数字的顺序比较他们

 

注:基本上每种运算符都有特殊情况,上面只是列举了一部分  (他们的特殊情况及相应的规则要特别注意)

 

es函数

1.arguments对象相当于形参的参数列表

2.函数可以作为参数传给另一个函数

3.function对象的length属性的值是函数期望的参数的个数

4.function对象的方法 valueOf()方法和toString方法,返回的都是函数的源代码

5.闭包:函数可以使用函数之外定义的变量

 

ecmascript面向对象技术

1.面向对象:

封装、聚集、继承、多态

2.宿主对象:
所有非本地对象都是宿主对象,(由ECMAScript实现的宿主环境提供的对象,如:BOM和DOM)

3.没有私有作用域,规定应该把在属性前后加下划线,看作是私有的,但事实上它还是公有的

没有静态作用域,可以给构造函数提供属性和方法(构造函数只是函数,函数是对象,对象可以有属性和方法)

4.关键字this总是指向调用该方法的对象

5.prototype属性可以定义构造函数的属性和方法,还可以为本地对象添加属性和方法

 

es继承

1.对象冒充可以支持多重继承

弊端:

 

例如,如果存在两个类 ClassX 和 ClassY,ClassZ 想继承这两个类,可以使用下面的代码:

 

function ClassZ() {
    this.newMethod = ClassX;
    this.newMethod();
    delete this.newMethod;

    this.newMethod = ClassY;
    this.newMethod();
    delete this.newMethod;
}
如果存在两个类 ClassX 和 ClassY 具有同名的属性或方法,ClassY 具有高优先级。因为它从后面的类继承。

 

call方法

第一个参数用作this对象,其他参数都直接传递给函数自身

例:

 

function sayColor(sPrefix,sSuffix) {
    alert(sPrefix + this.color + sSuffix);
};

var obj = new Object();
obj.color = "blue";

sayColor.call(obj, "The color is ", "a very nice color indeed.");

 

 

apply方法

 

apply() 方法有两个参数,用作 this 的对象和要传递给函数的参数的数组。例如:

 

function sayColor(sPrefix,sSuffix) {
    alert(sPrefix + this.color + sSuffix);
};

var obj = new Object();
obj.color = "blue";

sayColor.apply(obj, new Array("The color is ", "a very nice color indeed."));

 

 

 

ES6:

 

1.let命令

let与var的区别

(let的作用域只在let的代码段中有效,且是在变量声明之后有效,在变量声明之前,该变量都是不可用的,成为暂时性死区)

let变量不会像var那样发生“变量提升”现象,所以变量一定要在声明后使用

例:

 

var tmp = 123;

if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}

 

上面代码中,存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。

 

let不允许在相同作用域内重复声明同一个变量,也不允许在函数内部重新声明参数

 

es5     函数只能在顶层作用域和函数作用域中声明,不能再块级作用域声明

es6允许在块级作用域中声明函数

 

const命令声明一个只读的常量,且一旦声明,常量的值就不能改变,作用域与let相同,只在声明所在的块级作用域内有效

常量储存的只是一个地址,不可改变的知识这个地址,但是对象本身是可变的

下面是一个将对象彻底冻结的函数。

var constantize = (obj) => {
  Object.freeze(obj);
  Object.keys(obj).forEach( (key, value) => {
    if ( typeof obj[key] === 'object' ) {
      constantize( obj[key] );
    }
  });
};

 

es6中一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,依旧是全局对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于全局对象的属性

 

例:

var a = 1; // 如果在Node的REPL环境,可以写成global.a // 或者采用通用方法,写成this.a

window.a // 1 let b = 1; window.b // undefined

 

 

es6变量的解构赋值

例:

let [foo, [[bar], baz]] = [1, [[2], 3]]; foo // 1 bar // 2 baz // 3

let [ , , third] = ["foo", "bar", "baz"]; third // "baz"

let [x, , y] = [1, 2, 3]; x // 1 y // 3

let [head, ...tail] = [1, 2, 3, 4]; head // 1 tail // [2, 3, 4]

let [x, y, ...z] = ['a']; x // "a" y // undefined z // []

 

如果等号的右边不是数组(或者严格地说,不是可遍历的结构,参见《Iterator》一章),那么将会报错。

// 报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};

 

解构赋值允许指定默认值。

var [foo = true] = [];
foo // true

[x, y = 'b'] = ['a']; // x='a', y='b'
[x, y = 'b'] = ['a', undefined]; // x='a', y='b'

es6内部使用严格相等的运算符,判断一个位置是否有值,如果一个成员不严格等于undefined,默认值是不会产生的
var [x = 1] = [undefined]; x // 1
var [x = 1] = [null]; x // null



如果默认值是一个表达式,这个表达式是惰性求值的,即只在用到的时候,才会求值
function f() {
  console.log('aaa');
}

let [x = f()] = [1];

上面代码中,因为x能取到值,所以函数f根本不会执行


对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

var { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"

var { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined

变量名与属性名不一致:
var { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"

let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'

这实际上说明,对象的解构赋值是下面形式的简写

var { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };

也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。

var { foo: baz } = { foo: "aaa", bar: "bbb" };
baz // "aaa"
foo // error: foo is not defined

上面代码中,真正被赋值的是变量baz,而不是模式foo


注意,采用这种写法时,变量的声明和赋值是一体的。对于letconst来说,变量不能重新声明,所以一旦赋值的变量以前声明过,就会报错。

let foo;
let {foo} = {foo: 1}; // SyntaxError: Duplicate declaration "foo"

let baz;
let {bar: baz} = {bar: 1}; // SyntaxError: Duplicate declaration "baz"

上面代码中,解构赋值的变量都会重新声明,所以报错了。不过,因为var命令允许重新声明,所以这个错误只会在使用letconst命令时出现。如果没有第二个let命令,上面的代码就不会报错。

 

数值和布尔值的解构赋值

解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。

let {toString: s} = 123;
s === Number.prototype.toString // true

let {toString: s} = true;
s === Boolean.prototype.toString // true

上面代码中,数值和布尔值的包装对象都有toString属性,因此变量s都能取到值。

解构赋值的规则是,只要等号右边的值不是对象,就先将其转为对象。由于undefinednull无法转为对象,所以对它们进行解构赋值,都会报错。

let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError


函数参数的解构赋值
[[1, 2], [3, 4]].map(([a, b]) => a + b); // [ 3, 7 ]


圆括号问题

不能使用圆括号的情况

以下三种解构赋值不得使用圆括号。

(1)变量声明语句中,不能带有圆括号。

// 全部报错
var [(a)] = [1];

var {x: (c)} = {};
var ({x: c}) = {};
var {(x: c)} = {};
var {(x): c} = {};

var { o: ({ p: p }) } = { o: { p: 2 } };

上面三个语句都会报错,因为它们都是变量声明语句,模式不能使用圆括号。

(2)函数参数中,模式不能带有圆括号。

函数参数也属于变量声明,因此不能带有圆括号。

// 报错
function f([(z)]) { return z; }


(3)赋值语句中,不能将整个模式,或嵌套模式中的一层,放在圆括号之中。

// 全部报错
({ p: a }) = { p: 42 };
([a]) = [5];

上面代码将整个模式放在圆括号之中,导致报错。

// 报错
[({ p: a }), { x: c }] = [{}, {}];

上面代码将嵌套模式的一层,放在圆括号之中,导致报错。

可以使用圆括号的情况

可以使用圆括号的情况只有一种:赋值语句的非模式部分,可以使用圆括号。

[(b)] = [3]; // 正确
({ p: (d) } = {}); // 正确
[(parseInt.prop)] = [3]; // 正确

上面三行语句都可以正确执行,因为首先它们都是赋值语句,而不是声明语句;其次它们的圆括号都不属于模式的一部分。第一行语句中,模式是取数组的第一个成员,跟圆括号无关;第二行语句中,模式是p,而不是d;第三行语句与第一行语句的性质一致。


变量解构赋值的用途:
1.交换变量的值
2,从函数返回多个值
3.函数参数的定义
4.提取json数据
5.函数参数的默认值
6.遍历map结构
7.输入模块的指定方法
详情见https://www.w3cschool.cn/ecmascript/3yhi1q5h.html最后部分


es6字符串扩展

JavaScript共有6种方法可以表示一个字符。

'\z' === 'z'  // true
'\172' === 'z' // true
'\x7A' === 'z' // true
'\u007A' === 'z' // true
'\u{7A}' === 'z' // true


es6数组的扩展
Map和Set数据结构有一个has方法,has方法与includes的区别
  • Map结构的has方法,是用来查找键名的,比如Map.prototype.has(key)WeakMap.prototype.has(key)Reflect.has(target, propertyKey)
  • Set结构的has方法,是用来查找值的,比如Set.prototype.has(value)WeakSet.prototype.has(value)

 

数组的空位

Array(3) // [, , ,]

返回一个具有3个空位的数组

注:空位不是undefined,一个位置的值等于undefined,依然是有值的,空位是没有任何值

 

es5与es6对空位的处理不一样

es6将空位转为undefined

 

 

函数扩展

参数默认值的位置

通常情况下,定义了默认值的参数,应该是函数的尾参数。如果非尾部的参数设置默认值,实际上这个参数是没办法省略的

有默认值的参数都不是尾参数,这时无法省略该参数,而不省略它后面的参数,除非显示输入undefined

 

函数指定了默认值后,将会返回没有指定默认值参数的个数(指定了默认值后,length属性将失真)

如果设置了默认值的参数 不是尾参数,那么length属性也不再计入后面的参数了

 

作用域:

如果默认值是一个变量,则该变量所处的作用域与其他变量的作用域规则是一样的:先是当前函数的作用域,然后才是全局作用域

 
 阅读(1423)  收藏  赞(0)   我要纠错 
 

ECMAScript 6 函数的扩展

由 youj 创建,最后一次修改 2016-09-20 15:53:44

函数参数的默认值

基本用法

在ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法。

function log(x, y) {
  y = y || 'World';
  console.log(x, y);
}

log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello World

上面代码检查函数log的参数y有没有赋值,如果没有,则指定默认值为World。这种写法的缺点在于,如果参数y赋值了,但是对应的布尔值为false,则该赋值不起作用。就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。

为了避免这个问题,通常需要先判断一下参数y是否被赋值,如果没有,再等于默认值。

if (typeof y === 'undefined') {
  y = 'World';
}

ES6允许为函数的参数设置默认值,即直接写在参数定义的后面。

function log(x, y = 'World') {
  console.log(x, y);
}

log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello

可以看到,ES6的写法比ES5简洁许多,而且非常自然。下面是另一个例子。

function Point(x = 0, y = 0) {
  this.x = x;
  this.y = y;
}

var p = new Point();
p // { x: 0, y: 0 }

除了简洁,ES6的写法还有两个好处:首先,阅读代码的人,可以立刻意识到哪些参数是可以省略的,不用查看函数体或文档;其次,有利于将来的代码优化,即使未来的版本在对外接口中,彻底拿掉这个参数,也不会导致以前的代码无法运行。

参数变量是默认声明的,所以不能用letconst再次声明。

function foo(x = 5) {
  let x = 1; // error
  const x = 2; // error
}

上面代码中,参数变量x是默认声明的,在函数体中,不能用letconst再次声明,否则会报错。

与解构赋值默认值结合使用

参数默认值可以与解构赋值的默认值,结合起来使用。

function foo({x, y = 5}) {
  console.log(x, y);
}

foo({}) // undefined, 5
foo({x: 1}) // 1, 5
foo({x: 1, y: 2}) // 1, 2
foo() // TypeError: Cannot read property 'x' of undefined

上面代码使用了对象的解构赋值默认值,而没有使用函数参数的默认值。只有当函数foo的参数是一个对象时,变量xy才会通过解构赋值而生成。如果函数foo调用时参数不是对象,变量xy就不会生成,从而报错。如果参数对象没有y属性,y的默认值5才会生效。

下面是另一个对象的解构赋值默认值的例子。

function fetch(url, { body = '', method = 'GET', headers = {} }) {
  console.log(method);
}

fetch('http://example.com', {})
// "GET"

fetch('http://example.com')
// 报错

上面代码中,如果函数fetch的第二个参数是一个对象,就可以为它的三个属性设置默认值。

上面的写法不能省略第二个参数,如果结合函数参数的默认值,就可以省略第二个参数。这时,就出现了双重默认值。

function fetch(url, { method = 'GET' } = {}) {
  console.log(method);
}

fetch('http://example.com')
// "GET"

上面代码中,函数fetch没有第二个参数时,函数参数的默认值就会生效,然后才是解构赋值的默认值生效,变量method才会取到默认值GET

再请问下面两种写法有什么差别?

// 写法一
function m1({x = 0, y = 0} = {}) {
  return [x, y];
}

// 写法二
function m2({x, y} = { x: 0, y: 0 }) {
  return [x, y];
}

上面两种写法都对函数的参数设定了默认值,区别是写法一函数参数的默认值是空对象,但是设置了对象解构赋值的默认值;写法二函数参数的默认值是一个有具体属性的对象,但是没有设置对象解构赋值的默认值。

// 函数没有参数的情况
m1() // [0, 0]
m2() // [0, 0]

// x和y都有值的情况
m1({x: 3, y: 8}) // [3, 8]
m2({x: 3, y: 8}) // [3, 8]

// x有值,y无值的情况
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]

// x和y都无值的情况
m1({}) // [0, 0];
m2({}) // [undefined, undefined]

m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]

参数默认值的位置

通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。

// 例一
function f(x = 1, y) {
  return [x, y];
}

f() // [1, undefined]
f(2) // [2, undefined])
f(, 1) // 报错
f(undefined, 1) // [1, 1]

// 例二
function f(x, y = 5, z) {
  return [x, y, z];
}

f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // 报错
f(1, undefined, 2) // [1, 5, 2]

上面代码中,有默认值的参数都不是尾参数。这时,无法只省略该参数,而不省略它后面的参数,除非显式输入undefined

如果传入undefined,将触发该参数等于默认值,null则没有这个效果。

function foo(x = 5, y = 6) {
  console.log(x, y);
}

foo(undefined, null)
// 5 null

上面代码中,x参数对应undefined,结果触发了默认值,y参数等于null,就没有触发默认值。

函数的length属性

指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2

上面代码中,length属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。比如,上面最后一个函数,定义了3个参数,其中有一个参数c指定了默认值,因此length属性等于3减去1,最后得到2

这是因为length属性的含义是,该函数预期传入的参数个数。某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了。同理,rest参数也不会计入length属性。

(function(...args) {}).length // 0

如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。

(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1

作用域

一个需要注意的地方是,如果参数默认值是一个变量,则该变量所处的作用域,与其他变量的作用域规则是一样的,即先是当前函数的作用域,然后才是全局作用域。

var x = 1;

function f(x, y = x) {
  console.log(y);
}

f(2) // 2

上面代码中,参数y的默认值等于x。调用时,由于函数作用域内部的变量x已经生成,所以y等于参数x,而不是全局变量x

如果调用时,函数作用域内部的变量x没有生成,结果就会不一样。

let x = 1;

function f(y = x) {
  let x = 2;
  console.log(y);
}

f() // 1

上面代码中,函数调用时,y的默认值变量x尚未在函数内部生成,所以x指向全局变量。

如果此时,全局变量x不存在,就会报错。

function f(y = x) {
  let x = 2;
  console.log(y);
}

f() // ReferenceError: x is not defined

下面这样写,也会报错。

var x = 1;

function foo(x = x) {
  // ...
}

foo() // ReferenceError: x is not defined

上面代码中,函数foo的参数x的默认值也是x。这时,默认值x的作用域是函数作用域,而不是全局作用域。由于在函数作用域中,存在变量x,但是默认值在x赋值之前先执行了,所以这时属于暂时性死区(参见《let和const命令》一章),任何对x的操作都会报错。

如果参数的默认值是一个函数,该函数的作用域是其声明时所在的作用域。请看下面的例子。

let foo = 'outer';

function bar(func = x => foo) {
  let foo = 'inner';
  console.log(func()); // outer
}

bar();

上面代码中,函数bar的参数func的默认值是一个匿名函数,返回值为变量foo。这个匿名函数声明时,bar函数的作用域还没有形成,所以匿名函数里面的foo指向外层作用域的foo,输出outer

如果写成下面这样,就会报错。

function bar(func = () => foo) {
  let foo = 'inner';
  console.log(func());
}

bar() // ReferenceError: foo is not defined

上面代码中,匿名函数里面的foo指向函数外层,但是函数外层并没有声明foo,所以就报错了。

下面是一个更复杂的例子。

var x = 1;
function foo(x, y = function() { x = 2; }) {
  var x = 3;
  y();
  console.log(x);
}

foo() // 3

上面代码中,函数foo的参数y的默认值是一个匿名函数。函数foo调用时,它的参数x的值为undefined,所以y函数内部的x一开始是undefined,后来被重新赋值2。但是,函数foo内部重新声明了一个x,值为3,这两个x是不一样的,互相不产生影响,因此最后输出3

如果将var x = 3var去除,两个x就是一样的,最后输出的就是2

var x = 1;
function foo(x, y = function() { x = 2; }) {
  x = 3;
  y();
  console.log(x);
}

foo() // 2

 

利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。

function throwIfMissing() {
  throw new Error('Missing parameter');
}

function foo(mustBeProvided = throwIfMissing()) {
  return mustBeProvided;
}

foo()
// Error: Missing parameter

 

 

rest参数(...变量名)中的变量代表一个数组,所以数组特有的方法都可以用于这个变量。下面是一个利用rest参数改写数组push方法的例子。

function push(array, ...items) {
  items.forEach(function(item) {
    array.push(item);
    console.log(item);
  });
}

var a = [];
push(a, 1, 2, 3)

注意,rest参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。函数的length属性不包括rest参数

 

扩展运算符 (...),它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列

例:

function push(array, ...items)

  {

    array.push(...items);

   }

function add(x, y)

  {

    return x + y;

   }

var numbers = [4, 38];

add(...numbers) // 42

 

如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错

const [...butLast, last] = [1, 2, 3, 4, 5]; // 报错

const [first, ...middle, last] = [1, 2, 3, 4, 5]; // 报错

 

 

var f = () => 5;

// 等同于

var f = function () { return 5 };

var sum = (num1, num2) => num1 + num2;

// 等同于

var sum = function(num1, num2)

{

   return num1 + num2;

 };

 

 

由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号。

var getTempItem = id => ({ id: id, name: "Temp" });

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



























 

















posted @ 2017-07-02 21:47  lin_zone  阅读(935)  评论(0编辑  收藏  举报