JS

你不知道的 JS(中) 第一篇

Posted by Bibooo on 10-02,2022
  • 空值(null)
  • 未定义(undefined)
  • 布尔值(boolean)
  • 数字(number)
  • 字符串(string)
  • 对象(object)
  • 符号(symbol,ES6 新增)

除对象外,其他属于统称基本类型了。

null 有点特殊, typeof对它的处理有点问题:正确的返回结果应该是 null,但这个 bug 由来已久,也许永远不会恢复,因为牵扯到太多的 Web系统,“修复”它会产生更多 bug


typeof function a(){} ==> “function”

Function 函数,实际上 object 一个”子类型“ 函数是“可调用对象”。它有个内部属性 [[call]] ,该属性使它可以被调用。

函数不仅是对象,还有属性 比如 length

数组也是对象确切地说,它是 object 的一个 “子类型”

delete

使用 delete删除数组元素要注意,数组属性length 不会发生改变。

在稀缺数组(含有空白单元的数组)空白数组可能会导致出人意料的结果。

数组也是对象,当包含字符串键值和属性的时候,(不计算在数组长度中。)

值得注意的是当字符串键值可以强制类型转换为 10进制数字的时候,它是会当成数字索引来对待。

字符串

JS 中的字符串和字符数组并不是一回事,只是看上去相似。

它们都是类数组,都有 length 属性 以及 indexoF concat 方法。

字符串不可变是指字符串的成员函数不会改变原始值,而是创建并返回一个新的字符串,数组成员函数都是在原始值上操作··

let a = '123';
c = a.toUpperCase();
a === c //false

虽然字符串没有这些函数但字符串可以“借用”数组的非更方法处理字符串。

let a = '123';
let c = Array.protoype.join.call(a,'-') 	

数组

JavaScript 引用指向的是值,如果一个值有 10 个引用,这些引用指向的都是同一个值,它们相互之间没有什么关系。

let a = ['123'];
let b = a;
b.push('4');
console.log(a,b) // ['123','4'] ['123','4']

b a 是[‘123’] 的两个不同引用,它们仅仅是指向值,而不是持有,当 push,它们都将指向新值 [‘123’,'4]

let a = ['123'];
let b = a;
b= ['345']
console.log(a,b) //123 345

引用指向的是值,而不是变量,所以一个引用无法更改另外一个引用的指向。

原生函数

常见的原生函数有:

  • String()
  • Number()
  • Boolean()
  • Array()
  • object()
  • Function()
  • RegExp()
  • Date()
  • Error()
  • Symbol()

构造函数

var a = new Array(3);
console.log(a.length) //3

构造函数Array 不要求必须带 new 关键字,Array 构造函数只带一个数字参数时,该参数会被作为数组的预设长度(length)。

var a = new Array(3);
var b = [undefined,undefined,undefined]

image-20221002195925371
在谷歌浏览器中。

Symbol

基本数据类型 --符号(Symbol).符号是具有唯一性的特殊值(并非绝对),用它来命名对象属性不容易导致重名。不是对象

var a = Symbol("abc") //不带new
 var sol = Symbol('hello');
 var a = {};
a[sol] = 'foobar';

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

强制类型转换

值类型转换

将值从一种类型转换为另一种类型通常称为类型转换,这是显示的情况,隐式的情况称为强制类型转换

var a = 42;
var b = a + '';//隐式强制类型转换
var c = String( a );//显示强制类型转换
JSON.stringify(undefined)  undefined
JSON.stringify(null)  null
JSON.stringify(function(){}) undefined
JSON.stringify([1,undefined,function(){},4]) // [1,null,null,4]
JSON.stringify({a:2,b:undefined,c:function(){},undefined,c:['123']}) //{"a":2,"c":["123"]}

JSON.stringfy(…) 在对象中遇到 undefined,function,和 symbol 时会自动将其忽略,在数组中则会返回 null (以保证单元位置不变)。

对包含循环引用的对象执行 JSON.stringfy() 会出错。

如果要对非法JSON值对象做字符串化,或者对象中的某些值无法被序列号,就需要定义 toJSON() 方法来返回一个安全的 JSON值

  var o = {}
    var a = {
        c:1,
        b:'123',
        d:['123'],
        o:a,
        toJSON:function(){
            return {c:this.c}
        }
    }
    o.e = a;
    console.log( JSON.stringify(a))

toJSON 是个自定义的方法,它只是帮我们返回一个适当的值,或者是我们想要的值,然后再用 JSON.stringfy() 对其进行字符串化。

**我们可以向 JSON.stringfy() 传递一个可选参数,它可以是数组,或者是函数 **

如果是数组,那么它必须是一个字符串数组,包括序列号处理的对象名称。除此之外其他的属性则被忽略。

如果是函数,它会对对象本身调用一次,然后对对象中的各个属性各调用一次,每次传递两个参数,键和值。如果要忽略某个键就返回 undefined,否则返回值得的值。

  var a = {
        c:1,
        b:'123',
        d:['123']
    }
    
    console.log(JSON.stringify(a,['c','b']))
    console.log(JSON.stringify(a,function(v,h){
       if(v !== "c"){
        return h
       }
    }))

JSON.stringify 还可以传递第三个参数

console.log(JSON.stringify(a,null,"---"))
{
---"c": 1,
---"b": "123",
---"d": [
------"123"
---]
}

JSON.stringify() 并不是强制类型转换。在这里介绍是因为它涉及 ToString 强制类型转换。

  1. 字符串,数字,布尔值和 null 的JSON.stringfy() 规则与 ToString 基本相同。
  2. 如果传递 JSON.stringfy() 的对象中定义了 toJSON() 方法,那么该方法会在字符串化前调用,以便将对象转换为安全的 JSON 值。

toBoolean

JavaScript 有两个关键词 true,false,分别代表布尔类型的真和假,我们常误以为数值 1 和 0 分别等同与 true 和 false。在有些语言可能是这样的,但在 JavaScript 中布尔值和数字是不一样的。

var a = new Boolean(false);
var b = new Number(0);
var c = new String("");

字符串和数字之间的显示转换

var a = 42;

var b = a.toString();
a.toString()是显示的,不过其中涉及隐式转换。因为 toString() 对42 这样的基本数据类型不适用,所以 JavaSctipt 引擎会自动 42 创建一个封装对象,然后对该对象调用 toString()。这里显式转换中含有隐式转换。

小方法

我们常用下面的方法来获得当前的时间戳:

var time = +new Date();

不过最好还是使用 ES5 中新加入的静态方法 Date.now()

 var a = 42;
if(Boolean(42)){
 console.log('123')
}

建议使用 Boolean 和 !! 来进行显示强制类型转换。

逻辑运算符 || 和 &&

逻辑运算符 ||或 &&与 ,在 JavaScript 中的表现有一些非常重要但却不太为人所知的细微差别。

我其他不太赞同将它们称为 “逻辑运算符”和 “选择器运算符”

为什么? 因为和其他语言不同,在 JS 中它们返回的并不是布尔值。

&& 和 || 运算符的返回值并不一定是布尔类型,而是两个操作数其他一个的值。

var a =42;
var b = "abc"
var c= null;
a || b   42
a && b   'abc'
c || b   'abc'
c && b   'null'

|| 和 && 首先会对第一个操作数 a c执行条件判断 如果不是 布尔值就会 ToBoolean 强制类型转换然后再执行条件判断

对于 || 来说,如果条件判断结果为 true 就返回第一个操作数 (a,c)的值,如果为 false就返回第一个操作数 (a,c)的值

&&则相反

符号的强制类型转换

symbol 不能被强制类型转换为数字,会报错,但是可以被强制类型转换为布尔值。

宽松相等 和 严格相等

**常见的误区是 “== 检查值是否相等” === 检查值和类型是否相等。**听起来蛮有道理的,然而不够准确,很多的 JavaScript 书籍和博客也是这样来解释的,但是很遗憾他们都错了

👂这是不是在说我 =-=

正确的解释是: == 允许是相等比较中可以进行强制类型转换, === 是不允许

相等比较操作的性能

两种解释

  • === 似乎比 == 做的事情更多,因为它还要检查值的类型。
  • == 的工作量更大一些,因为如果值的类型不同还需要进行强制类型转换

如果进行比较的两个值类型相同,则 == 和 === 使用相同的算法,所以除了 JavaScript 引擎 实现上的细微差别之外,它们之间并没用什么不同。

如果两个值的类型不同,我们就需要考虑有没有强制类型转换的必要,有就用 ==,没有就3个 = ,不用在乎性能。

== 最容易出错的地方

var a = "32";
var b = true;
a == b //false

我们都知道 "32"是一个真值,为什么 == 的结果不是 true 呢 ? 原因既简单又复杂,让人很容易掉坑里,很多 JavaScript 开发人员对这个地方并未引起足够的重视。

  1. 如果 type(x) 是布尔类型,则返回 ToNumber(x) == y的结果
  2. 如果 type(y)是布尔类型,则返回 x == ToNumber(y) 的结果

b 强制类型转换为 1 , a 强制类型转换为 32 1 == 32 //false

不要这样判断

var a = '32';
if( a ==true )
if ( a === true )
if (a)
if(!!a)
if(boolean(a))

null 和 undefined 之间的相等比较

null 和 undefined 之间也涉及隐式类型转换

null == undefined // true

undefined== null //true

在 == 中,null 和 undefined相等,它们与其自身相等,除此在外,它们与其它值都不相等

对象和非对象之间的比较

var a = 42;

var b = [42];

a ==b ;// true

[ 42 ] 首先会调用 ToPrimitive 抽象操作, 返回 “42” “42” == 42 然后 42 == 42

ToPrimitive 抽象操作 特性(toString,valueOf)

**关于显式和隐式就介绍到这里了,总结 **

==是相等比较下允许使用强制类型转换,三个=是不允许


这为一篇,因为 《《你不知道的JS》》中册有 358页,也算很厚的了,所以我是打算分为 4 篇来做,本人只是记录一些我比较感兴趣的知识,和一些我知识点的错误,推荐大家还是看原书。