zoukankan      html  css  js  c++  java
  • React-Native_02:语法篇

    1.简单介绍


    ECMAScript 6.0(以下简称ES6)是JavaScript语言的下一代标准,已经在2015年6月正式公布了。它的目标。是使得JavaScript语言能够用来编写复杂的大型应用程序,成为企业级开发语言。

    JavaScript的创造者Netscape公司,之后将JavaScript提交给国际标准化组织ECMA。希望这种语言能够成为国际标准。ECMAScript和JavaScript的关系是,前者是后者的规格,后者是前者的一种实现,

    之所以不叫JavaScript。有两个原因。


    一是商标。Java是Sun公司的商标,依据授权协议,仅仅有Netscape公司能够合法地使用JavaScript这个名字,且JavaScript本身也已经被Netscape公司注冊为商标。
    二是想体现这门语言的制定者是ECMA,不是Netscape。这样有利于保证这门语言的开放性和中立性。

    在线将ES6代码转为ES5代码(https://babeljs.io/repl/)
    Babel是一个广泛使用的ES6转码器,能够将ES6代码转为ES5代码,从而在现有环境运行。这意味着。你能够用ES6的方式编敲代码,又不用操心现有环境是否支持。

    2.变量声明
    ES5仅仅有2种声明变量的方法:var、function。
    ES6共同拥有6种声明变量的方法:var、function、let、const、import(require)、class。

    2.1 var命令

    var a = 10;
    var b = 20;
    var c = 30;

    var a = 10,b = 20,c = 30;

    var arr = [1,2,3,4,5];
    var a = arr[0];
    var b = arr[1];
    var c = arr[3];

    var obj = {
      name: 'gary',
      age: 20
    }
    var a = obj.name;
    var b = obj.age;

    没实用varkeyword。使用直接赋值方式声明的是全局变量,比如:
    a = 10;

    全局对象是最顶层的对象,在浏览器环境指的是window对象,在Node.js指的是global对象。

    ES5之中,全局对象的属性与全局变量是等价的。
    window.a = 1;
    a // 1
    a = 2;
    window.a // 2



    2.2 function命令
    var 方式定义的函数,不能先调用函数。后声明,仅仅能先声明函数。然后调用。
    function方式定义函数能够先调用,后声明。

    aaa();//这样调用就会出错   
    var aaa = function(){   
          alert("aaa");   
      }   
    aaa();//这样就不会出错   
        
    //先调用后声明   
    bbb();   
    function bbb(){   
          alert("bbb");   
    }   


    2.3 let命令

    块级有效
    ES5仅仅有全局作用域和函数作用域,没有块级作用域,在ES6中,let实际上为JavaScript新增了块级作用域。
    用来声明变量,使用方法相似于var,可是所声明的变量,仅仅在let命令所在的代码块内有效。

    function f1() {
      let n = 5;
      if (true) {
        let n = 10;
      }
      console.log(n); // 5
    }

    for循环的计数器,就很合适使用let命令。比如:
    var a = [];
    for (var i = 0; i < 10; i++) {
      a[i] = function () {
        console.log(i);
      };
    }
    a[5](); //10
    a[6](); // 10

    var a = [];
    for (let i = 0; i < 10; i++) {
      a[i] = function () {
        console.log(i);
      };
    }
    a[5](); //5
    a[6](); // 6

    变量提升
    let不像var那样会发生“变量提升”现象??

    ????


    console.log(foo); // 输出undefined
    console.log(bar); // 报错ReferenceError
    var foo = 2;
    let bar = 2;

    实測结果两个都是undefined,应该是网上资料错误,能够通过Babel来了解底层原理


    临时性死区
    仅仅要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
    var tmp='dev';
    if (true) { 
      console.log(tmp);

      let tmp; 
      console.log(tmp);

      tmp = 123;
      console.log(tmp);
    }
    在let命令声明变量tmp之前,都属于变量tmp的“死区”。




    不同意反复声明
    let不同意在同样作用域内,反复声明同一个变量。
    // 报错
    function test() {
      let a = 10;
      var a = 1;
    }
    // 报错
    function test() {
      let a = 10;
      let a = 1;
    }
    因此,不能在函数内部又一次声明參数。
    function func(arg) {
      let arg; // 报错
    }
    function func(arg) {
      {
        let arg; // 不报错
      }
    }


    2.4 const命令

    const声明一个仅仅读的常量。一旦声明,就必须马上初始化。不能留到以后赋值。

    也不能改变。


    const PI = 3.1415;
    console.log(PI); // 3.1415
    PI = 3;// TypeError: Assignment to constant variable.

    const的作用域与let命令同样:仅仅在声明所在的块级作用域内有效。声明的常量,也与let一样不可反复声明。

    const命令仅仅是保证变量名指向的地址不变,并不保证该地址的数据不变,所以将一个对象声明为常量必须很小心。
    const foo = {};
    foo.prop = 123;
    console.log(foo.prop);// 123
    foo = {}; // TypeError: "foo" is read-only

    var命令和function命令声明的全局变量,依然是全局对象的属性。
    let命令、const命令、class命令声明的全局变量,不属于全局对象的属性。也就是说。从ES6開始。全局变量将逐步与全局对象的属性脱钩。



    var a = 1;
    // 假设在Node的REPL环境,能够写成global.a
    // 或者採用通用方法。写成this.a
    window.a // 1
    let b = 1;
    window.b // undefined


    2.5 import命令

    模块的功能主要由 export 和 import 组成.每个模块都有自己单独的作用域。模块之间的相互调用关系是通过 export 来规定模块对外暴露的接口,通过import来引用其他模块提供的接口。

    同一时候还为模块创造了命名空间。防止函数的命名冲突。

    ES6将一个文件视为一个模块,通过export 向外输出了一个变量。一个模块也能够同一时候往外面输出多个变量。
    //test.js
    var name = 'Rainbow';
    var age = '24';
    export {name, age};

    定义好模块的输出以后就能够在另外一个模块通过import引用。


    import {name, age} from './test.js'

    总体输入,module指令
    //test.js
    export function getName() {
      return name;
    }
    export function getAge(){
      return age;

    通过 import * as 就完毕了模块总体的导入。
    import * as test form './test.js';

    通过指令 module 也能够达到总体的输入。
     module test from 'test.js';
     test.getName();

    export default
    不用关系模块输出了什么。通过 export default 指令就能载入到默认模块。不须要通过 花括号来指定输出的模块,一个模块仅仅能使用 export default 一次

    // default 导出
    export default function getAge() {} 
     
    // 或者写成
    function getAge() {}
    export default getAge;

    // 导入的时候不须要花括号
    import test from './test.js';

    一条import 语句能够同一时候导入默认方法和其他变量.
    import defaultMethod, { otherMethod } from 'xxx.js';

    2.6 class命令

    假设你用过纯面向对象语言,那么你会对class的语法很熟悉。


    class People {
        constructor(name) { //构造函数
              this.name = name;
        }
        sayName() {
              console.log(this.name);
        }
    }

    var p = new People("Tom");
    p.sayName();

    上面定义了一个People类,他有一个属性 name 和一个方法 sayName(),另一个构造函数; 
    就像函数有函数声明和函数表达式两种定义方式,类也能够通过类表达式来定义:
    let People = class {
        constructor(name) { //构造函数
              this.name = name;
        }
        sayName() {
              console.log(this.name);
        }
      }

    你可能以为类声明和类表达式的差别在于变量提升的不同。可是事实是不管是类声明还是类表达式的方式来定义,都不会有变量提升。


    通过keyword extends 来继承一个类。并且,能够通过 super keyword来引用父类。
    class Student extends People {
        constructor(name, grade) { //构造函数
            super(name);    //通过 super 调用父类的构造函数的。
              this.grade = grade;
        }
        sayGrade() {
              console.log(this.grade);
        }
    }
    上面的样例中我们定义了一个 Student 。他是 People 的子类。




    以下我们给 name 属性定义 getter 和 setter
    class People {
        constructor(name) { //构造函数
              this.name = name;
        }
        get name() {
            return this._name.toUpperCase();
        }
        set name(name) {
            this._name = name;
        }
        sayName() {
              console.log(this.name);
        }
    }
    var p = new People("tom");
    console.log(p.name);    //TOM
    console.log(p._name);    //tom
    p.sayName();    //TOM
    细致看上面的样例,搞清晰最后三行分别会输出什么,就明确getter 和 setter该怎么用了。



    主要是要区分 this._name 和 this.name 的差别。由于我们定义了 name 的读写器,而未定义 _name 的读写器。所以訪问这两个属性的结果是不同的。

    可是要注意一点,不要这样写:

    set name(name) {
        this.name = name;
    }
    由于给 this.name 赋值的时候会调用 set name ,这样会导致无限递归直到栈溢出。

    通过 static keyword定义静态方法:
    class People {
        constructor(name) { //构造函数
              this.name = name;
        }
        sayName() {
              console.log(this.name);
        }
        static formatName(name) {
            return name[0].toUpperCase() + name.sustr(1).toLowerCase();
        }
    }
    console.log(People.formatName("tom"));


    3.解构赋值

    ES6同意依照一定模式。从数组和对象中提取值。对变量进行赋值。这被称为解构(Destructuring)。

    “模式匹配”,仅仅要等号两边的模式同样,左边的变量就会被赋予相应的值。以下是一些使用嵌套数组进行解构的样例。
    假设解构不成功,变量的值就等于undefined。


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

    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 // []

    解构赋值同意指定默认值。
    var [foo = true] = [];
    foo // true

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

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

    注意。ES6内部使用严格相等运算符(===)。推断一个位置是否有值。所以,假设一个数组成员不严格等于undefined。默认值是不会生效的。



    默认值能够引用解构赋值的其他变量,但该变量必须已经声明。
    let [x = 1, y = x] = [];     // x=1; y=1
    let [x = 1, y = x] = [2];    // x=2; y=2
    let [x = 1, y = x] = [1, 2]; // x=1; y=2
    let [x = y, y = 1] = [];     // ReferenceError

    解构赋值不仅适用于var命令,也适用于let和const命令。


    var [v1, v2, ..., vN ] = array;
    let [v1, v2, ..., vN ] = array;
    const [v1, v2, ..., vN ] = array;

    对于Set结构。也能够使用数组的解构赋值。


    let [x, y, z] = new Set(["a", "b", "c"]);
    x // "a"

    对象的解构赋值
    解构不仅能够用于数组。还能够用于对象。


    var { foo, bar } = { foo: "aaa", bar: "bbb" };
    foo // "aaa"
    bar // "bbb"
    对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定。而对象的属性没有次序,变量必须与属性同名,才干取到正确的值。


    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

    变量的声明和赋值是一体的。

    对于let和const来说,变量不能又一次声明,所以一旦赋值的变量曾经声明过,就会报错。
    let foo;
    let {foo} = {foo: 1}; // SyntaxError: Duplicate declaration "foo"
    let baz;
    let {bar: baz} = {bar: 1}; // SyntaxError: Duplicate declaration "baz"
    上面代码中。解构赋值的变量都会又一次声明,所以报错了。只是,由于var命令同意又一次声明。所以这个错误仅仅会在使用let和const命令时出现。假设没有第二个let命令。上面的代码就不会报错。
    let foo;
    ({foo} = {foo: 1}); // 成功
    let baz;
    ({bar: baz} = {bar: 1}); // 成功
    上面代码中,let命令以下一行的圆括号是必须的,否则会报错。由于解析器会将起首的大括号。理解成一个代码块,而不是赋值语句。
     
    和数组一样,解构也能够用于嵌套结构的对象。
    var obj = {
      p: [
        'Hello',
        { y: 'World' }
      ]
    };
    var { p: [x, { y }] } = obj;
    x // "Hello"
    y // "World"

    注意,这时p是模式,不是变量,因此不会被赋值。
    var node = {
      loc: {
        start: {
          line: 1,
          column: 5
        }
      }
    };
    var { loc: { start: { line }} } = node;
    line // 1
    loc  // error: loc is undefined
    start // error: start is undefined

    默认值生效的条件是。对象的属性值严格等于undefined。



    假设要将一个已经声明的变量用于解构赋值。必须很小心。
    // 错误的写法
    var x;
    {x} = {x: 1};
    // SyntaxError: syntax error
    上面代码的写法会报错。由于JavaScript引擎会将{x}理解成一个代码块。从而发生语法错误。仅仅有不将大括号写在行首。避免JavaScript将其解释为代码块,才干解决问题。
    // 正确的写法
    ({x} = {x: 1});

    由于数组本质是特殊的对象,因此能够对数组进行对象属性的解构。


    var arr = [1, 2, 3];
    var {0 : first, [arr.length - 1] : last} = arr;
    first // 1
    last // 3

    字符串的解构赋值
    字符串也能够解构赋值。

    这是由于此时,字符串被转换成了一个相似数组的对象。
    const [a, b, c, d, e] = 'hello';
    a // "h"
    b // "e"
    c // "l"
    d // "l"
    e // "o"
    相似数组的对象都有一个length属性。因此还能够对这个属性解构赋值。
    let {length : len} = 'hello';
    len // 5

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


    let {toString: s} = 123;
    s === Number.prototype.toString // true
    let {toString: s} = true;
    s === Boolean.prototype.toString // true
    上面代码中,数值和布尔值的包装对象都有toString属性。因此变量s都能取到值。

    解构赋值的规则是。仅仅要等号右边的值不是对象,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。
    let { prop: x } = undefined; // TypeError
    let { prop: y } = null; // TypeError

    函数參数的解构也能够使用默认值。


    function move({x = 0, y = 0} = {}) {
      return [x, y];
    }
    move({x: 3, y: 8}); // [3, 8]
    move({x: 3}); // [3, 0]
    move({}); // [0, 0]
    move(); // [0, 0]


    函数move的參数是一个对象,通过对这个对象进行解构,得到变量x和y的值。

    假设解构失败。x和y等于默认值。


    function move({x, y} = { x: 0, y: 0 }) {
      return [x, y];
    }
    move({x: 3, y: 8}); // [3, 8]
    move({x: 3}); // [3, undefined]
    move({}); // [undefined, undefined]
    move(); // [0, 0]

    函数move的參数指定默认值,而不是为变量x和y指定默认值,所以会得到与前一种写法不同的结果。

    其实,仅仅要某种数据结构具有Iterator接口,都能够採用数组形式的解构赋值。
    function* fibs() {
      var a = 0;
      var b = 1;
      while (true) {
        yield a;
        [a, b] = [b, a + b];
      }
    }
    var [first, second, third, fourth, fifth, sixth] = fibs();
    sixth // 5

    解构赋值用途:

    (1)交换变量的值
    [x, y] = [y, x];

    上面代码交换变量x和y的值。这种写法不仅简洁。并且易读,语义很清晰。


    (2)从函数返回多个值
    函数仅仅能返回一个值。假设要返回多个值,仅仅能将它们放在数组或对象里返回。有了解构赋值,取出这些值就很方便。
    // 返回一个数组
    function example() {
      return [1, 2, 3];
    }
    var [a, b, c] = example();
    // 返回一个对象
    function example() {
      return {
        foo: 1,
        bar: 2
      };
    }

    var { foo, bar } = example();


    (3)函数參数的定义

    解构赋值能够方便地将一组參数与变量名相应起来。
    // 參数是一组有次序的值
    function f([x, y, z]) { ... }
    f([1, 2, 3]);
    // 參数是一组无次序的值
    function f({x, y, z}) { ... }

    f({z: 3, y: 2, x: 1});


    (4)提取JSON数据

    解构赋值对提取JSON对象中的数据,尤其实用。
    var jsonData = {
      id: 42,
      status: "OK",
      data: [867, 5309]
    };
    let { id, status, data: number } = jsonData;
    console.log(id, status, number);
    // 42, "OK", [867, 5309]

    上面代码能够高速提取JSON数据的值。


    (5)函数參数的默认值
    jQuery.ajax = function (url, {
      async = true,
      beforeSend = function () {},
      cache = true,
      complete = function () {},
      crossDomain = false,
      global = true,
      // ... more config
    }) {
      // ... do stuff
    };

    指定參数的默认值,就避免了在函数体内部再写var foo = config.foo || 'default foo';这种语句。


    (6)遍历Map结构
    不论什么部署了Iterator接口的对象,都能够用for...of循环遍历。Map结构原生支持Iterator接口,配合变量的解构赋值。获取键名和键值就很方便。


    var map = new Map();
    map.set('first', 'hello');
    map.set('second', 'world');
    for (let [key, value] of map) {
      console.log(key + " is " + value);
    }
    // first is hello
    // second is world
    假设仅仅想获取键名,或者仅仅想获取键值,能够写成以下这样。
    // 获取键名
    for (let [key] of map) {
      // ...
    }
    // 获取键值
    for (let [,value] of map) {
      // ...

    }


    (7)输入模块的指定方法
    载入模块时。往往须要指定输入那些方法。解构赋值使得输入语句很清晰。


    const { SourceMapConsumer, SourceNode } = require("source-map");

    4.编程风格
    4.1 採用严格模式:'use strict';

    主要有以下限制:
    变量必须声明后再使用
    函数的參数不能有同名属性,否则报错
    不能使用with语句
    不能对仅仅读属性赋值,否则报错
    不能使用前缀0表示八进制数,否则报错
    不能删除不可删除的属性,否则报错
    不能删除变量delete prop。会报错,仅仅能删除属性delete global[prop]
    eval不会在它的外层作用域引入变量
    eval和arguments不能被又一次赋值
    arguments不会自己主动反映函数參数的变化
    不能使用arguments.callee
    不能使用arguments.caller
    禁止this指向全局对象
    不能使用fn.caller和fn.arguments获取函数调用的堆栈
    添加了保留字(比方protected、static和interface)


    4.2 let代替var

    在块级作用域下,let全然能够代替var。由于两者语义同样。并且let没有副作用。

    4.3 全局常量和线程安全

    在let和const之间,建议优先使用const,尤其是在全局环境。不应该设置变量,仅仅应设置常量。全部的函数都应该设置为常量。这符合函数式编程思想。有利于将来的分布式运算。
    const声明常量还有两个优点,一是阅读代码的人立马会意识到不应该改动这个值,二是防止了无意间改动变量值所导致的错误。


    长远来看。JavaScript可能会有多线程的实现(比方Intel的River Trail那一类的项目),这时let表示的变量,仅仅应出如今单线程运行的代码中,不能是多线程共享的,这样有利于保证线程安全。



    4.4 字符串
    静态字符串一律使用单引號或反引號,不使用双引號。动态字符串使用反引號。

    4.5 解构赋值
    使用数组成员对变量赋值时,优先使用解构赋值。

    函数的參数假设是对象的成员,优先使用解构赋值。

    假设函数返回多个值,优先使用对象的解构赋值,而不是数组的解构赋值。这样便于以后加入返回值,以及更改返回值的顺序。

    4.6 对象
    单行定义的对象。最后一个成员不以逗号结尾。

    多行定义的对象。最后一个成员以逗号结尾。

    对象尽量静态化。一旦定义。就不得任意加入新的属性。假设加入属性不可避免,要使用Object.assign方法。

    假设对象的属性名是动态的,能够在创造对象的时候。使用属性表达式定义。

    另外,对象的属性和方法。尽量採用简洁表达法,这样易于描写叙述和书写。

    4.7 数组
    使用扩展运算符(...)拷贝数组。



    使用Array.from方法,将相似数组的对象转为数组。



    4.8 函数
    马上运行函数能够写成箭头函数的形式。

    那些须要使用函数表达式的场合,尽量用箭头函数代替。

    由于这样更简洁。并且绑定了this。

    简单的、单行的、不会复用的函数,建议採用箭头函数。假设函数体较为复杂。行数较多。还是应该採用传统的函数写法。



    全部配置项都应该集中在一个对象,放在最后一个參数。布尔值不能够直接作为參数。



    不要在函数体内使用arguments变量。使用rest运算符(...)代替。由于rest运算符显式表明你想要获取參数,并且arguments是一个相似数组的对象,而rest运算符能够提供一个真正的数组。

    4.9 Map结构
    注意区分Object和Map。仅仅有模拟现实世界的实体对象时,才使用Object。假设仅仅是须要key: value的数据结构,使用Map结构。由于Map有内建的遍历机制。



    4.10 Class
    总是用Class,代替须要prototype的操作。由于Class的写法更简洁,更易于理解。



    使用extends实现继承,由于这样更简单,不会有破坏instanceof运算的危急。

    4.11 模块
    首先。Module语法是JavaScript模块的标准写法,坚持使用这种写法。

    使用import代替require。



    假设模块仅仅有一个输出值。就使用export default,假设模块有多个输出值。就不使用export default,不要export default与普通的export同一时候使用。



    不要在模块输入中使用通配符。由于这样能够确保你的模块之中,有一个默认输出(export default)。

    假设模块默认输出一个对象。对象名的首字母应该大写。

    5.网络资源
    http://www.w3school.com.cn/js/index.asp
    http://es6.ruanyifeng.com/
    http://babeljs.io/

  • 相关阅读:
    【Java】推断文件的后缀名
    UVa 131
    Java开发手冊 Java学习手冊教程(MtJava开发手冊)
    《Java并发编程实战》第十五章 原子变量与非堵塞同步机制 读书笔记
    OC语言--NSFileManager&amp; NSFileHandle
    为什么我刚发表的文章变成了“待审核”,csdn有没有官方解释啊
    mac os升级为 Yosemite 10.10 后不能创建javaproject
    【spring】在spring cloud项目中使用@ControllerAdvice做自定义异常拦截,无效 解决原因
    【mybatis】mybatis动态order by 的问题, 注意 只需要把#{} 改成 ${} 即可
    【spring cloud】一个ms微服务想要给注册中心eureka发现,需要满足这些条件,微服务不能被eureka注册中心发现的解决方案
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/8335229.html
Copyright © 2011-2022 走看看