You Don`t Know JS(中)--第一部分

类型

1、JavaScript 有七种内置类 型:null 、undefined 、boolean 、number 、string 、object 和 symbol ,可以使用 typeof 运算符来查看。
2、变量没有类型,但它们持有的值有类型。类型定义了值的行为特征。
3、很多开发人员将 undefined 和 undeclared 混为一谈,但在 JavaScript 中它们是两码事。undefined 是值的一种。undeclared 则表 示变量还没有被声明过。 遗憾的是,JavaScript 却将它们混为一谈,在我们试图访问 “undeclared” 变量时这样报错:ReferenceError: a is not defined, 并且 typeof 对 undefined 和 undeclared 变量都返回 “undefined” 。
然而,通过 typeof 的安全防范机制(阻止报错)来检查 undeclared 变量,有时是个不错的办法。

1、JavaScript 中的数组是通过数字索引的一组任意类型的值。字符串和数组类似,但是它们的行为特征不同,在将字符作为数组
来处理时需要特别小心。JavaScript 中的数字包括“整数”和“浮点型”。
2、基本类型中定义了几个特殊的值。
null 类型只有一个值 null ,undefined 类型也只有一个值 undefined 。所有变量在赋值之前默认值都是 undefined 。void 运算符返回 undefined 。
数字类型有几个特殊值,包括 NaN (意指“not a number”,更确切地说是“invalid number”)、+Infinity 、-Infinity 和 -0 。
3、简单标量基本类型值(字符串和数字等)通过值复制来赋值 / 传递,而复合值(对象等)通过引用复制来赋值 / 传递。 JavaScript 中的引用和其他语言中的引用 / 指针不同,它们不能指向别的变量 / 引用,只能指向值

原生函数

1、JavaScript 为基本数据类型值提供了封装对象,称为原生函数(如 String 、Number 、Boolean 等)。它们为基本数据类 型值提供了该子类型所特有的方法和属性(如:String#trim() 和 Array#concat(..) )。

  • 内部属性 [[Class]]
Object.prototype.toString.call( [1,2,3] ); 
// "[object Array]"
Object.prototype.toString.call( /regex-literal/i ); 
// "[object RegExp]"
Object.prototype.toString.call( null );
// "[object Null]"
Object.prototype.toString.call( undefined );
 // "[object Undefined]"
  • 如果想要自行封装基本类型值,可以使用 Object(..) 函数(不带 new 关键字):
var a = "abc"; 
var b=newString(a); 
var c=Object(a);
typeof a; // "string" 
typeof b; // "object" 
typeof c; // "object"
b instanceof String; // true 
c instanceof String; // true
Object.prototype.toString.call( b ); // "[object String]" Object.prototype.toString.call( c ); // "[object String]"
  • 拆封:如果想要得到封装对象中的基本类型值,可以使用 valueOf() 函数:
var a = new String( "abc" ); 
var b = new Number( 42 );
var c = new Boolean( true );
a.valueOf(); // "abc" 
b.valueOf(); // 42 
c.valueOf(); // true
  • 原生函数作为构造函数
vara=newArray(3);
var b = [ undefined, undefined, undefined ]; 
var c=[];
c.length = 3;
a; b; c;
a.join( "-" ); // "--" b.join( "-" ); // "--"
a.map(function(v,i){ return i; }); // [ undefined x 3 ] b.map(function(v,i){ return i; }); // [ 0, 1, 2 ]
  • symbol
var mysym = Symbol( "my own symbol" );
mysym; // Symbol(my own symbol)
mysym.toString(); //"Symbol(myownsymbol)" 
typeof mysym; // "symbol"
var a={}; 
a[mysym] = "foobar";
Object.getOwnPropertySymbols( a ); 
// [ Symbol(my own symbol) ]

虽然符号实际上并非私有属性(通过 Object.getOwnPropertySymbols(..) 便可以公开获得对象中的所有符号),但 它却主要用于私有或特殊属性。很多开发人员喜欢用它来替代有下划线(_ )前缀的属性,而下划线前缀通常用于命名私有或 特殊属性。

  • 原生原型
    原生构造函数有自己的 .prototype 对象,如 Array.prototype 、String.prototype 等。 这些对象包含其对应子类型所特有的行为特征。
    String.prototype.XYZ 简写为 String#XYZ ,对其他 .prototypes 也同样如此。 String#indexOf(..)
    在字符串中找到指定子字符串的位置。
    String#charAt(..)
    获得字符串指定位置上的字符。
    String#substr(..) 、String#substring(..) 和 String#slice(..) 获得字符串的指定部分。
    String#toUpperCase() 和 String#toLowerCase() 将字符串转换为大写或小写。
    String#trim()
    去掉字符串前后的空格,返回新的字符串。
    以上方法并不改变原字符串的值,而是返回一个新字符串。
typeof Function.prototype; // "function" 
Function.prototype();// 空函数!
RegExp.prototype.toString(); // "/(?:)/"——空正则表达式
"abc".match( RegExp.prototype ); // [""]

更糟糕的是,我们甚至可以修改它们(而不仅仅是添加属性):

Array.isArray(Array.prototype); //true 
Array.prototype.push( 1, 2, 3 ); // 3 
Array.prototype; // [1,2,3]
// 需要将Array.prototype设置回空,否则会导致问题! 
Array.prototype.length = 0;

这里,Function.prototype 是一个函数,RegExp.prototype 是一个正则表达式,而 Array.prototype 是一个数 组。是不是很有意思?

  • 将原型作为默认值
    Function.prototype 是一个空函数,RegExp.prototype 是一个“空”的正则表达式(无任何匹配),而 Array.prototype 是一个空数组。对未赋值的变量来说,它们是很好的默认值
function isThisCool(vals,fn,rx) {
  vals = vals || Array.prototype; 
  rx = rx || RegExp.prototype;
  return rx.test(
  vals.map( fn ).join( "" )
  ); 
}
isThisCool(); // true
isThisCool( 
  ["a","b","c"],
  function(v){ return v.toUpperCase(); }, /D/
);// false

2、对于简单标量基本类型值,比如 “abc” ,如果要访问它的 length 属性或 String.prototype 方法,JavaScript 引擎会自动对该值进行封装(即用相应类型的封装对象来包装它)来实现对这些属性和方法的访问。

强制类型转换

一、值类型转换

  • 显示隐式示例
var a=42;

var b = a + ""; // 隐式强制类型转换
var c = String( a ); // 显式强制类型转换 
  • toString() 可以被显式调用,或者在需要字符串化时自动调用。所有安全的 JSON 值 (JSON-safe)都可以使用 JSON.stringify(..) 字符串化。安全的 JSON 值是指能够呈现为有效 JSON 格式的值。
JSON.stringify(undefined); // undefined 
JSON.stringify(function(){}); //undefined
JSON.stringify([1, undefined, function () {}, 4]); 
// "[1,null,null,4]"
JSON.stringify({
            a: 2,
            b: function () {}
        });// "{"a":2}"
  • toNumber
       var a = {
            valueOf: function () {
                return "42";
            }
        };
        var b = {
            toString: function () {
                return "42";
            }
        };
        var c = [4, 2];
        c.toString = function () {
                return this.join(""); // "42" };
                Number(a); // 42
                Number(b); // 42
                Number(c); // 42
                Number(""); // 0
                Number([]); // 0
                Number(["abc"]); // NaN
  • ToBoolean
    以下这些是假值:
    undefined
    null
    
false
    
+0、-0和NaN “”
    假值的布尔强制类型转换结果为 false 。

二、 显式强制类型转换

扫描二维码关注公众号,回复: 2465336 查看本文章
  • 显式解析数字字符串
        var a = "42"
        var b = "42px";
        Number(a); // 42 parseInt( a ); // 42
        Number(b); // NaN parseInt( b ); // 42
  • 解析非字符串
parseInt( 1/0, 19 ); // 18 

parseInt(1/0, 19) 实际上是 parseInt(“Infinity”, 19) 。第一个字符是 “I” ,以 19 为基数时值为 18 。第二个 字符 “n” 不是一个有效的数字字符,解析到此为止,和 “42px” 中的 “p” 一样。 最后的结果是 18 ,而非 Infinity 或者报错。所以理解其中的工作原理对于我们学习 JavaScript 是非常重要的

        parseInt(0.000008);//0 ("0"来自于"0.000008")
        parseInt(0.0000008); // 8 ("8" 来自于 "8e-7")
        parseInt(false, 16); // 250 ("fa" 来自于 "false")
        parseInt(parseInt, 16); //15("f"来自于"function..")
        parseInt("0x10");  // 16 
        parseInt("103", 2); // 2
  • 显式转换为布尔值
        var a = "0";
        var b = [];
        var c = {};
        var d = "";
        var e = 0;
        var f = null;
        var g;
        Boolean(a); // true
        Boolean(b); // true
        Boolean(c); // true
        Boolean(d); // false 
        Boolean(e); // false
        Boolean(f); // false
        Boolean(g); // false

三、隐式强制类型转换
隐式强制类型转换 指的是那些隐蔽的强制类型转换,副作用也不是很明显。换句话说,你自己觉得不够明显的强制类型转换 都可以算作隐式强制类型转换。
- 字符串和数字之间的隐式强制类型转换

var a = [1,2]; 
var b = [3,4]; 
a + b; // "1,23,4" 

原因:+ 将进行拼接操作。如果其中一 个操作数是对象(包括数组),则首先对其调用 ToPrimitive 抽象操作,该抽象操作再调用

 var a = {
            valueOf: function () {
                return 42;
            },
            toString: function () {
                return 4;
            }
        };
        a + ""; // "42" String( a ); // "4"
var a=[3];
var b=[1]; 
a-b;//2 
  • 布尔值到数字的隐式强制类型转换
     function onlyOne(a, b, c) {
            return !!((a && !b && !c) ||
                (!a && b && !c) || (!a && !b && c));
        }
        var a = true;
        var b = false;
        onlyOne(a, b, b); // true onlyOne( b, a, b ); // true
        onlyOne(a, b, a); // false
  • 隐式强制类型转换为布尔值
    下面的情况会发生布尔值隐式强制类型转换。
    (1)if (..)语句中的条件判断表达式。
    (3)while (..)和do..while(..)循环中的条件判断表达式。
    (4)? :中的条件判断表达式。
    (5) 逻辑运算符 || (逻辑或)和 && (逻辑与)左边的操作数(作为条件判断表达式)。
    以上情况中,非布尔值会被隐式强制类型转换为布尔值,遵循前面介绍过的ToBoolean 抽象操作规则
        var a = 42;
        var b = "abc";
        var c;
        var d = null;
        if (a) {
            console.log("yep");
        }
        // yep
        while (c) {
            console.log("nope, never runs");
        }
        c = d ? a : b;
        c;
        if ((a && d) || c) {
            console.log("yep");
        }
        // "abc"
        // yep
  • || 和 &&
    a||b;
// 大致相当于(roughly equivalent to): a?a:b;
    a&&b;
// 大致相当于(roughly equivalent to): a?b:a;

  • 符号的强制类型转换

var s1 = Symbol( "cool" );

String( s1 ); // "Symbol(cool)" 
var s2 = Symbol( "not cool" ); 
s2 + ""; // TypeError 

四、宽松相等和严格相等
- null undefined false “”

      var a = null;
        var b;
        a == b;// true
        a == null;// true
        b == null;// true
        a == false; // false
        b == false; // false
        a == ""; // false
        b == ""; // false
        a == 0; // false
        b == 0; // false
var a=42; 
var b=[42]; 
a==b;//true 

[ 42 ] 首先调用 ToPromitive 抽象操作(参见 4.2 节),返回 “42” ,变成 “42” == 42 ,然后又变成 42 == 42 , 最后二者相等。

         var a =null
         var b=Object(a);// 和Object()一样
         a==b;// false

         var c=undefined;
         var d=Object(c);// 和Object()一样
         c==d;// false

         var e=NaN;
         var f=Object(e);// 和new Number( e )一样
         e==f;// false

因为没有对应的封装对象,所以 null 和 undefined 不能够被封装(boxed),Object(null) 和 Object() 均返回一个 常规对象。
NaN 能够被封装为数字封装对象,但拆封之后 NaN == NaN 返回 false ,因为 NaN 不等于 NaN (参见第 2 章)

  • 比较少见的情况
        "0" == null; // false
        "0" == undefined; // false
        "0" == false; // true -- 晕! // false
        "0" == NaN; // true
        "0" == 0; // false
        "0" == ""; // false
        false == null; // false
        false == undefined; // false
        false == NaN; // true -- 晕!
        false == 0; // true -- 晕!
        false == ""; // true -- 晕! 
        false == []; // false
        false == {}; // false
        "" == null; // false
        "" == undefined; // false
        "" == NaN; //true--晕! 
        "" == 0; // true -- 晕! 
        "" == []; // false
        "" == {}; // false
        0 == null; // false
        0 == undefined; // false
        0 == NaN; // false
        0 == []; //true--晕! 
        0 == {}; // false
         2 == [2]; // true
        ""==[null]; //true 
        []==![] //true 
        0 == "\n"; // true 

五、抽象关系比较

var a=["42"]; 
var b=["043"]; 
a<b; //false 
var a=[4,2]; 
var b=[0,4,3]; 
a<b; //false 
var a={b:42}; 
var b={b:43}; 
a<b; //false
 a==b;//false 
a>b; //false 
a<=b;//true 
a>=b;//true 

因为根据规范a <= b被处理为b < a,然后将结果反转。因为b < a的结果是false,所以a <= b的结果是true

var a=[42]; 
var b = "043"; 
a < b; // false -- 字符串比较!
Number( a ) < Number( b ); // true -- 数字比较! 

语法

语句和表达式在英语中都能找到类比——语句就像英语中的句子,而表达式就像短语。表达式可以是简单独立的,否则可能会 产生副作用
1、代码块

[] + {}; // "[object Object]" 
{}+[];//0 

第一行代码中,{} 出现在 + 运算符表达式中,因此它被当作一个值(空对象)来处理。第 4 章讲过 [] 会被强制类型转换为
“” ,而 {} 会被强制类型转换为 “[object Object]” 。但在第二行代码中,{} 被当作一个独立的空代码块(不执行任何操作)。代码块结尾不需要分号,所以这里不存在语法上的 问题。最后 + [] 将 [] 显式强制类型转换 (参见第 4 章)为 0 。
2、运算符的优先级
下表按从最高到最低的优先级列出JavaScript运算符。具有相同优先级的运算符按从左至右的顺序求值。
这里写图片描述
3、对 ES6 中的参数默认值而言,参数被省略或被赋值为 undefined 效果都一样,都是取该参数的默认值。然而某些情况下, 它们之间还是有区别的:

        function foo(a = 42, b = a + 1) {
            console.log(
                arguments.length, a, b,
                arguments[0], arguments[1]
                );
        }
        foo(); // 0 42 43 undefined undefined 
        foo( 10 ); // 1 10 11 10 undefined 
        foo(10,undefined); //2101110undefined
        foo(10, null); // 2 10 null 10 null

4、try..finally

     function foo() {
            try {
                throw 42;
            } finally {
                console.log("Hello");

            }
            console.log("never runs");
        }
            console.log(foo());
            // Hello
            // Uncaught Exception: 42
           function foo() {
            try {
                return 42;
            } finally {
                throw "Oops!";
            }
            console.log("never runs");
            console.log(foo());
            // Uncaught Exception: Oops!

finally 中的 return 会覆盖 try 和 catch 中 return 的返回值:

      function foo() {
            try {
                return 42;

            } finally {
                // 没有返回语句,所以没有覆盖
            }
        }

        function bar() {
            try {
                return 42;
            } finally {
                // 覆盖前面的 return 42 return;
            }
        }

        function baz() {
            try {
                return 42;
            } finally {
                // 覆盖前面的 return 42 return "Hello";
            }
        }
        foo(); // 42 
        bar(); // undefined 
        baz(); // Hello

猜你喜欢

转载自blog.csdn.net/michellezhai/article/details/81061534
今日推荐