zoukankan      html  css  js  c++  java
  • 再看JavaScript

    阅读阮一峰的JavaScript 教程,过基础,记录我想记录的笔记。

    网页地址https://wangdoc.com/javascript/

    1.null 和 undefined

    1995年 JavaScript 诞生时,最初像 Java 一样,只设置了null表示"无"。根据 C 语言的传统,null可以自动转为0。

    JavaScript 的设计者 Brendan Eich,觉得这样做还不够。首先,第一版的 JavaScript 里面,null就像在 Java 里一样,

    被当成一个对象,Brendan Eich 觉得表示“无”的值最好不是对象。其次,那时的 JavaScript 不包括错误处理机制,Brendan Eich 觉得,如果null自动转为0,很不容易发现错误。

    因此,他又设计了一个undefined区别是这样的null是一个表示“空”的对象,转为数值时为0undefined是一个表示"此处无定义"的原始值,转为数值时为NaN。

    null 表示为空值,即该处的值现在为空。

    undefined表示“未定义”,变量声明了,但没有赋值。

    2.布尔值

    如果 JavaScript 预期某个位置应该是布尔值,会将该位置上现有的值自动转为布尔值。转换规则是除了下面六个值被转为false,其他值都视为true

    • undefined
    • null
    • false
    • 0
    • NaN
    • ""''(空字符串)
    
    
    if ('') {
      console.log('true');
    }
    // 没有任何输出
    
    

     注意,空数组([])和空对象({})对应的布尔值,都是true

    
    
    if ([]) {
      console.log('true');
    }
    // true
    
    if ({}) {
      console.log('true');
    }
    // true
    
    
    
    

    3.整数和浮点数

    JavaScript 内部,所有数字都是以64位浮点数形式储存,即使整数也是如此。所以,11.0是相同的,是同一个数。

    
    
    1 === 1.0 // true


    这就是说,JavaScript 语言的底层根本没有整数,所有数字都是小数(64位浮点数)。容易造成混淆的是,某些运算只有整数才能完成,此时 JavaScript 会自动把64位浮点数,转成32位整数,然后再进行运算

    4.正零和负零

    JavaScript 内部实际上存在2个0:一个是+0,一个是-0,区别就是64位浮点数表示法的符号位不同。它们是等价的。

    唯一有区别的场合是,+0-0当作分母,返回的值是不相等的。

    
    
    (1 / +0) === (1 / -0) // false
    
    

    上面的代码之所以出现这样结果,是因为除以正零得到+Infinity,除以负零得到-Infinity,这两者是不相等的

    5.NaN

    NaN是 JavaScript 的特殊值,表示“非数字”(Not a Number),主要出现在将字符串解析成数字出错的场合。

    
    
    5 - 'x' // NaN

    上面代码运行时,会自动将字符串x转为数值,但是由于x不是数值,所以最后得到结果为NaN,表示它是“非数字”(NaN)。

    0除以0也会得到NaN

    
    
    0 / 0 // NaN
    
    

    需要注意的是,NaN不是独立的数据类型,而是一个特殊数值,它的数据类型依然属于Number,使用typeof运算符可以看得很清楚。

    
    
    typeof NaN // 'number'
    
    

    (2)运算规则

    NaN不等于任何值,包括它本身。

    
    
    NaN === NaN // false
    
    

    6.Infinity

    Infinity表示“无穷”,用来表示两种场景。一种是一个正的数值太大,或一个负的数值太小,无法表示;另一种是非0数值除以0,得到Infinity

    它的四则运算就不记录了。https://wangdoc.com/javascript/types/number.html 平常用不到。

    7.对象

    对象(object)是 JavaScript 语言的核心概念,也是最重要的数据类型。

    什么是对象?简单说,对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合。

    8.函数

    (1)function 命令

    
    
    function print(s) {
      console.log(s);
    }


    (2)函数表达式

    除了用function命令声明函数,还可以采用变量赋值的写法。

    
    
    var print = function(s) {
      console.log(s);
    };

     (3)Function 构造函数

    第三种声明函数的方式是Function构造函数。

    
    
    var add = new Function(
      'x',
      'y',
      'return x + y'
    );
    
    // 等同于
    function add(x, y) {
      return x + y;
    }


     这种声明函数的方式非常不直观,几乎无人使用。

    立即调用函数表达式(IIFE)

    在 JavaScript 中,圆括号()是一种运算符,跟在函数名之后,表示调用该函数。

    (function(){ /* code */ }())


    9.eval命令

    eval命令接受一个字符串作为参数,并将这个字符串当作语句执行。

    
    
    eval('var a = 1;');
    a // 1
    
    

    上面代码将字符串当作语句运行,生成了变量a

    eval没有自己的作用域,都在当前作用域内执行,因此可能会修改当前作用域的变量的值,造成安全问题。

    
    
    var a = 1;
    eval('a = 2');
    
    a // 2
    
    

    上面代码中,eval命令修改了外部变量a的值。由于这个原因,eval有安全风险。

    10.数组

    数组(array)是按次序排列的一组值。每个值的位置都有编号(从0开始),整个数组用方括号表示。

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

    上面代码中的abc就构成一个数组,两端的方括号是数组的标志。a是0号位置,b是1号位置,c是2号位置。

    数组本质是一个对象。

    10.指数运算符

    指数运算符(**)完成指数运算,前一个运算子是底数,后一个运算子是指数。

    
    
    2 ** 4 // 16
    
    

    注意,指数运算符是右结合,而不是左结合。即多个指数运算符连用时,先进行最右边的计算。

    
    
    // 相当于 2 ** (3 ** 2)
    2 ** 3 ** 2
    // 512

    上面代码中,由于指数运算符是右结合,所以先计算第二个指数运算符,而不是第一个。

    11.严格相等运算符

    avaScript 提供两种相等运算符:=====

    简单说,它们的区别是相等运算符(==)比较两个值是否相等,严格相等运算符(===)比较它们是否为“同一个值”。如果两个值不是同一类型,严格相等运算符(===)直接返回false,而相等运算符(==)会将它们转换成同一个类型,再用严格相等运算符进行比较。

    12.编程风格

    建议不要使用相等运算符(==),只使用严格相等运算符(===)。

    建议自增(++)和自减(--)运算符尽量使用+=-=代替。

    13.Object

    Object的实例方法

    所谓实例方法就是定义在Object原型对象Object.prototype上的方法。它可以被Object实例直接使用。

    Object.prototype.print = function () {
      console.log(this);
    };
    
    var obj = new Object();
    obj.print() // Object
     

     Object.keys方法的参数是一个对象,返回一个数组。该数组的成员都是该对象自身的(而不是继承的)所有属性名。

    var obj = {
      p1: 123,
      p2: 456
    };
    
    Object.keys(obj) // ["p1", "p2"]

    由于 JavaScript 没有提供计算对象属性个数的方法,所以可以用这个方法代替。

    var obj = {
      p1: 123,
      p2: 456
    };
    
    Object.keys(obj).length // 2

    一般情况下,几乎总是使用Object.keys方法,遍历对象的属性。

    14.控制对象状态

    有时需要冻结对象的读写状态,防止对象被改变。JavaScript 提供了三种冻结方法,最弱的一种是Object.preventExtensions,其次是Object.seal,最强的是Object.freeze

    Object.preventExtensions方法可以使得一个对象无法再添加新的属性

    Object.seal只是禁止新增或删除属性,并不影响修改某个属性的值。

    Object.freeze方法可以使得一个对象无法添加新属性、无法删除旧属性、也无法改变属性的值,使得这个对象实际上变成了常量。

    15.Array

    push方法用于在数组的末端添加一个或多个元素,并返回添加新元素后的数组长度。注意,该方法会改变原数组。

    var arr = [];
    
    arr.push(1) // 1
    arr.push('a') // 2
    arr.push(true, {}) // 4
    arr // [1, 'a', true, {}]

    pop方法用于删除数组的最后一个元素,并返回该元素。注意,该方法会改变原数组。

    var arr = ['a', 'b', 'c'];
    arr.pop() // 'c'
    arr // ['a', 'b']

    对空数组使用pop方法,不会报错,而是返回undefined

    [].pop() // undefined

    pushpop结合使用,就构成了“后进先出”的栈结构(stack)。

    shift()方法用于删除数组的第一个元素,并返回该元素。注意,该方法会改变原数组。

    var a = ['a', 'b', 'c'];
    a.shift() // 'a'
    a // ['b', 'c']

    unshift()方法用于在数组的第一个位置添加元素,并返回添加新元素后的数组长度。注意,该方法会改变原数组。

    var a = ['a', 'b', 'c'];
    a.unshift('x'); // 4
    a // ['x', 'a', 'b', 'c']

    push()shift()结合使用,就构成了“先进先出”的队列结构(queue)。

    concat方法用于多个数组的合并。它将新数组的成员,添加到原数组成员的后部,然后返回一个新数组,原数组不变。

    如果数组成员包括对象,concat方法返回当前数组的一个浅拷贝。所谓“浅拷贝”,指的是新数组拷贝的是对象的引用。

    var obj = { a: 1 };
    var oldArray = [obj];
    
    var newArray = oldArray.concat();
    
    obj.a = 2;
    newArray[0].a // 2

    上面代码中,原数组包含一个对象,concat方法生成的新数组包含这个对象的引用。所以,改变原对象以后,新数组跟着改变。

    小技巧concat用于拷贝数组

    reverse方法用于颠倒排列数组元素,返回改变后的数组。注意,该方法将改变原数组。

    var a = ['a', 'b', 'c'];
    
    a.reverse() // ["c", "b", "a"]
    a // ["c", "b", "a"]

    splice()方法用于删除原数组的一部分成员,并可以在删除的位置添加新的数组成员,返回值是被删除的元素。注意,该方法会改变原数组。

    arr.splice(start, count, addElement1, addElement2, ...);

    splice的第一个参数是删除的起始位置(从0开始),第二个参数是被删除的元素个数。如果后面还有更多的参数,则表示这些就是要被插入数组的新元素。

     15.Array

    JSON对象是 JavaScript 的原生对象,用来处理 JSON 格式数据。它有两个静态方法:JSON.stringify()JSON.parse()

    JSON.stringify方法用于将一个值转为 JSON 字符串。该字符串符合 JSON 格式,并且可以被JSON.parse方法还原。

     16.绑定this的方法

    函数实例的call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。

    var obj = {};
    
    var f = function () {
      return this;
    };
    
    f() === window // true
    f.call(obj) === obj // true
    func.call(thisValue, arg1, arg2, ...)

    call的第一个参数就是this所要指向的那个对象,后面的参数则是函数调用时所需的参数。

    apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数,使用格式如下。

    func.apply(thisValue, [arg1, arg2, ...])

    利用这一点,可以做一些有趣的应用。

    (1)找出数组最大元素
    
    JavaScript 不提供找出数组最大元素的函数。结合使用apply方法和Math.max方法,就可以返回数组的最大元素。
    var a = [10, 2, 4, 15, 9]; Math.max.apply(null, a) // 15
    (2)将数组的空元素变为undefined 通过apply方法,利用Array构造函数将数组的空元素变成undefined。 Array.apply(null, ['a', ,'b']) // [ 'a', undefined, 'b' ]

    bind()方法用于将函数体内的this绑定到某个对象,然后返回一个新函数。

    回调函数是 JavaScript 最常用的模式之一,但是一个常见的错误是,将包含this的方法直接当作回调函数。解决方法就是使用bind()方法

    17.单线程模型

    单线程模型指的是,JavaScript 只在一个线程上运行。也就是说,JavaScript 同时只能执行一个任务,其他任务都必须在后面排队等待。

    注意,JavaScript 只在一个线程上运行,不代表 JavaScript 引擎只有一个线程。事实上,JavaScript 引擎有多个线程,单个脚本只能在一个线程上运行(称为主线程),其他线程都是在后台配合。

    JavaScript 语言的设计者意识到,这时 CPU 完全可以不管 IO 操作,挂起处于等待中的任务,先运行排在后面的任务。等到 IO 操作返回了结果,再回过头,把挂起的任务继续执行下去。这种机制就是 JavaScript 内部采用的“事件循环”机制(Event Loop)。

    同步和异步任务

    程序里面所有的任务,可以分成两类:同步任务(synchronous)和异步任务(asynchronous)。
    
    同步任务是那些没有被引擎挂起、在主线程上排队执行的任务。只有前一个任务执行完毕,才能执行后一个任务。
    
    异步任务是那些被引擎放在一边,不进入主线程、而进入任务队列的任务。只有引擎认为某个异步任务可以执行了(比如 Ajax 操作从服务器得到了结果),该任务(采用回调函数的形式)才会进入主线程执行。排在异步任务后面的代码,不用等待异步任务结束会马上运行,也就是说,异步任务不具有“堵塞”效应。
    
    举例来说,Ajax 操作可以当作同步任务处理,也可以当作异步任务处理,由开发者决定。如果是同步任务,主线程就等着 Ajax 操作返回结果,再往下执行;如果是异步任务,主线程在发出 Ajax 请求以后,就直接往下执行,等到 Ajax 操作有了结果,主线程再执行对应的回调函数。
    同步和异步概念

    任务队列和事件循环

    JavaScript 运行时,除了一个正在运行的主线程,引擎还提供一个任务队列(task queue),里面是各种需要当前程序处理的异步任务。(实际上,根据异步任务的类型,存在多个任务队列。为了方便理解,这里假设只存在一个队列。)
    
    首先,主线程会去执行所有的同步任务。等到同步任务全部执行完,就会去看任务队列里面的异步任务。如果满足条件,那么异步任务就重新进入主线程开始执行,这时它就变成同步任务了。等到执行完,下一个异步任务再进入主线程开始执行。一旦任务队列清空,程序就结束执行。
    
    异步任务的写法通常是回调函数。一旦异步任务重新进入主线程,就会执行对应的回调函数。如果一个异步任务没有回调函数,就不会进入任务队列,也就是说,不会重新进入主线程,因为没有用回调函数指定下一步的操作。
    
    JavaScript 引擎怎么知道异步任务有没有结果,能不能进入主线程呢?答案就是引擎在不停地检查,一遍又一遍,只要同步任务执行完了,引擎就会去检查那些挂起来的异步任务,是不是可以进入主线程了。这种循环检查的机制,就叫做事件循环(Event Loop)。维基百科的定义是:“事件循环是一个程序结构,用于等待和发送消息和事件(a programming construct that waits for and dispatches events or messages in a program)”。
    任务队列和事件循环

    JavaScript的学习就记录到这里,内容其实还有很多很多很多...,不过现在不再过多深入了。

  • 相关阅读:
    Android ActionBar应用实战,高仿微信主界面的设计
    Android ActionBar完全解析,使用官方推荐的最佳导航栏(下) .
    Android ActionBar完全解析,使用官方推荐的最佳导航栏(上)
    actionBar兼容2.1及以上版本的做法 .
    Android UI开发详解之ActionBar .
    Android ActionBar详解(三):ActionBar实现切换Tabs标签
    Android ActionBar详解(二):ActionBar实现Tabs标签以及下拉导航 .
    完毕port(CompletionPort)具体解释
    Java的递归算法
    shell语法简单介绍
  • 原文地址:https://www.cnblogs.com/cdjbolg/p/12706206.html
Copyright © 2011-2022 走看看