第3章----之语法及数据类型

语法
一、区分大小写
ECMAScript中的一切(比如:变量名,函数名,操作符)都区分大小写。

二、标识符
指代的是变量名,函数名、属性名称、参数名称。一般规则有两个。
1、第一个字符必须是字母,下划线或则美元符号$,其他字符可以是字母,下划线,美元符号和数字。
2、命名一般规定俗成为驼峰式命名(比如:chuJiu)。

三、注释
1、单行------ //
2、多行

/*
* 多行
* 注释
*/

四、严格模式
使用'use strict'设置整个文件或则某个模块为严格模式,规范化代码。

五、语句
1、以分号结尾,若忽略分号,则由解析器确定语句结尾,为了防止某种不确定性,最好每条语句以分号结尾。
2、当ifelse尾部只有一条语句时,也要用花括号{}括起,有助于可读性。

数据类型
数据类型分为两种数据类型,一种为基本数据类型,另一种为引用数据类型。由于ES6新增加了一种基本数据类型Symbol
一、数据类型具体分类
1、基本数据类型
1.1、内容:Number、String、Boolean、Symbol、Undefined、Null
1.2、内存储存位置:储存在中,没有深,浅拷贝一说。

2、引用数据类型
2.1、内容:Object
2.2、内存存储位置:ObjectName存储在中,指向堆中实际数值集的存储位置;实际数据集存储在中,从而产生了深,浅拷贝的区分。

二、数据类型检测
1、typeof操作符

// 基本类型
console.log(typeof 111) // number
console.log(typeof '111') // string
console.log(typeof true) // boolean
console.log(typeof Symbol()) // symbol
console.log(typeof undefined) // undefined
console.log(typeof null) // object

// 引用类型
console.log(typeof {}) // object
console.log(typeof []) // object
console.log(typeof new Function()) // function

我们就很好奇为什么typeof nullobject,而在引用类型中只有objectfunction

1.1、为什么typeof nullobject
原理是这样的, 不同的对象在底层都表示为二进制, 在 JavaScript 中二进制前三位都为 0 的话会被判断为 object 类型, null 的二进制表示是全 0, 自然前三位也是 0, 所以执行 typeof 时会返回“object”

1.2、而关于引用类型为什么只有只有objectfunction

if (JSVAL_IS_VOID(v)) {  // (1)
    type = JSTYPE_VOID;
} else if (JSVAL_IS_OBJECT(v)) {  // (2)【注意了,从这开始 /】
    obj = JSVAL_TO_OBJECT(v);
    if (obj &&
        (ops = obj->map->ops,
            ops == &js_ObjectOps
            ? (clasp = OBJ_GET_CLASS(cx, obj),
            clasp->call || clasp == &js_FunctionClass) // (3,4)
            : ops->call != 0)) {  // (3)
        type = JSTYPE_FUNCTION;
    } else {
        type = JSTYPE_OBJECT;
    } // 【看这里,从这结尾 ///】
} else if (JSVAL_IS_NUMBER(v)) {
    type = JSTYPE_NUMBER;
} else if (JSVAL_IS_STRING(v)) {
    type = JSTYPE_STRING;
} else if (JSVAL_IS_BOOLEAN(v)) {
    type = JSTYPE_BOOLEAN;
}

这是第一版的 javascript 实现中,判断类型的代码。我们从上面可以看出,首先判断是否为引用类型,然后再判断是否为function,不为则全表示为object,这就是为什么typeof []typeof {}全表示为object

2、instanceof操作符

// 基本类型
console.log(111 instanceof Number) // false
console.log('111' instanceof String) // false
console.log(true instanceof Boolean) // false
console.log(Symbol() instanceof Symbol) // false
console.log(undefined instanceof undefined) 
// Uncaught TypeError: Right-hand side of 'instanceof' is not an object
console.log(null instanceof null)
// Uncaught TypeError: Right-hand side of 'instanceof' is not an object

// 引用类型
console.log({} instanceof Object) // true
console.log({} instanceof Array) // false
console.log({} instanceof Function) // false
console.log([] instanceof Object) // true
console.log([] instanceof Array) // true
console.log([] instanceof Function) // false
console.log(new Function() instanceof Object) // true
console.log(new Function() instanceof Function) // true
console.log(new Function() instanceof Array) // false

我们有几个疑惑,为什么同样为基本数据类型undefined instanceof undefinednull instanceof null就报错呢;为什么不报错的基本数据类型全部为false;对于引用类型为什么有些true而又有些false呢?

2.1、为什么undefined instanceof undefinednull instanceof null就报错呢?
我们得首先知道instanceof前后关系为前子后父(可能是父亲,可能是爷爷,也可能更加久远……),那么当js引擎解析到前面的数据无法证明它有父类(这个凭证就是__proto__【基本数据是隐藏,引用类型是显式】),它就会抛出异常Uncaught TypeError: Right-hand side of 'instanceof' is not an object。我们看段代码来验证我们的说法。

const num = 111
console.log(num.__proto__) // Number {0, constructor: ƒ, toExponential: ƒ, toFixed: ƒ, toPrecision: ƒ, …}
const str = '111'
console.log(str.__proto__) // String {"", constructor: ƒ, anchor: ƒ, big: ƒ, blink: ƒ, …}
const bol = true
console.log(bol.__proto__) // Boolean {false, constructor: ƒ, toString: ƒ, valueOf: ƒ}
const sys = Symbol()
console.log(sys.__proto__) // Symbol {constructor: ƒ, toString: ƒ, valueOf: ƒ, Symbol(Symbol.toStringTag): "Symbol", …}
const und = undefined
console.log(und.__proto__) // Uncaught TypeError: Cannot read property '__proto__' of undefined
const nul = null
console.log(nul.__proto__) // Uncaught TypeError: Cannot read property '__proto__' of null

2.2、为什么基本数据类型全是错的呢?
因为instanceof是通过显式的__proto__(打印儿子,控制台可以看见其存在__proto__属性)属性寻找儿子(前数据)原型连上的构造函数,但是所有的__proto__都是隐式的,因此全部判为false,所以instanceof只能用来大概的检测对象类型

2.3、为什么引用类型时错时对呢?
我们就一一说说上述三者的族谱(继承)关系吧。
键值对类型{} -》constructorObject()(父亲) -》constructorObject()(爷爷)
数组类型[] -》constructorArray()(父亲) -》constructorObject()(爷爷)
函数类型new Function() -》constructorFunction()(父亲) -》constructorObject()(爷爷)
从而我们就可以得知开始对于引用类型数据instanceof的各项结果了。

3、准确判断是为{},[],new Function()三者区别。
思想:思想本质一样,将父类(而不是最初类)构造函数字符串化,通过正则提取不同部分作为区分标识

const str = 'chujiu'
const obj = {}
const arr = []
const fun = new Function()

// 直接字符串化父类构造函数
const firstWay = data => {
  if( !(data instanceof Object) ) {
    return 'sorry, this is not a Object'
  }
  // 正则预查询
  const [ type ] = data.constructor.toString().match(/\w+(?=\(\))/g)
  return type
}

// 通过apply/bind绑定对象,在进行字符串化父类构造函数
const secondWay = data => {
  if( !(data instanceof Object) ) {
    return 'sorry, this is not a Object'
  }
  const [ commentType ,type ] = Object.prototype.toString.apply(data).match(/\w+/g)
  return type
}

console.log(firstWay(obj)) // Object
console.log(firstWay(arr)) // Array
console.log(firstWay(fun)) // Function
console.log(secondWay(obj)) // Object
console.log(secondWay(arr)) // Array
console.log(secondWay(fun)) // Function

三、Undefined类型
Undefined类型只有一个值,那就是undefined。值得注意的其就两点。
1、其为变量未初始值时的初始值
2、undefined派生与null,所以undefined == nulltrue,但是由于二者数据类型不同,因此undefined === nullfalse(强烈建议,实际开发中用绝对相等===代替普通相等==)。

四、Null类型
Null类型同样也只有一个值,那就是null。值得注意的其就一点。
1、从逻辑上讲,null指代的是一个空对象指针,这也侧面证明了typeof nullobject

五、Boolean类型
Boolean类型存在两个值,truefalse。值得注意的是其他数值Boolean值类型转化。
如图:
在这里插入图片描述
六、Number类型
Boolean类型存在三个值,整数、浮点数和NaN。
1、整数
最基本的数值字面量格式是十进制整数(默认也为十进制整数),但是除却十进制之外还有八进制,十六进制等其他进制整数。

1.1、八进制整数
在非严格模式中,字面量第一位一定是0,然后后面字面量范围在(0~7),如若超出这个范围,则前导0忽略,以十进制解析字面量;在严格模式,不允许八进制的存在,但是我们可以通过parseInt(numValue, 8)来变通实现

1.2、十六进制
在非严格/严格模式中,字面量的前两位一定是0x,然后后面字面量范围在(0~9,a-f,A-F【不区分大小写】),如若超出这个范围,则抛出语法错误Uncaught SyntaxError: Invalid or unexpected token

1.3、parseInt()方法
在处理整数时,我们会常用到parseInt()方法。parseInt()方法在转换字符串时,通常判定的是否符合数值模型。它会忽略字符串前面的空格,直到检测到第一个不为空的字符。若没有检测到不为空的字符或则没有传参时,则会返回NaN;若检测到第一个不为空的字符不符合数值模型或则不为+、-操作符时,则会返回NaN;若第一个字符符合数值模型,则会继续解析下一个字符,若不符合数值模型,则会停止解析,返回已解析数值

'use strict'
console.log(parseInt('010')) // 10
console.log(parseInt('10')) // 10
console.log(parseInt('0x10')) // 16
console.log(parseInt('10.1')) // 10
console.log(parseInt('-10.1')) // -10
console.log(parseInt('  -10')) // -10
console.log(parseInt('i10')) //NaN
console.log(parseInt('10 0')) // 10
console.log(parseInt()) //NaN
console.log(parseInt('  ')) //NaN
console.log(parseInt(true)) //NaN
console.log(parseInt(undefined)) //NaN

此外,parseInt()方法允许一个表示进制基数的参数传入,因为在parseInt()方法解析字面量为八进制时比如(parseInt('070')),ECMAScript3表示为56【八进制】,ECMA-Script5表示为70【忽略前导零,表示为十进制】。为了避免这种疑惑,parseInt()方法引入了第二个参数,表示字符串所转数值的进制。因此也就出现了进制的不同而导致的数值模型的不同

定义:parseInt(string, radix) 将一个字符串 string 转换为 radix 进制的整数, radix 为介于2-36之间的数(值得注意的是当radix写为0或则没有传入时,则表示为十进制)。

数值模型(第一个非空字符允许+,-操作符外):
二进制:只允许0~1
八进制:只允许0~7
十进制:只允许0~9
十六进制:只允许0~9,a(A)-f(F)
其他进制数值模型类推……

coding验证

console.log([1, 2, 3].map(parseInt)) // [1, NaN, NaN]

这段代码可以解析为

console.log([1, 2, 3].map((string, radix) => {
  // parseInt方法内部逻辑代码
}))

所以,当我们用parseInt方法作为map方法的参数(回调函数)时,parseInt方法参数stringmap回调函数参数的itemradixmap回调函数参数的index,因此代码可以进一步变通的解析为

console.log([1, 2, 3].map((item, index) => {
  return parseInt(item, index) 
}))

利用上述基数参数与数值模型关联结可以论证得最终结果为[1, NaN, NaN]。

2、浮点数
所谓浮点数,就是该数值中必须含有个小数点,并且小数点后面至少有一位数字。

2.1、浮点数默认解析为整数
因为保存浮点数是保存整数空间的两倍,因此ECMAScript会不失时机的将浮点数值转化为整数。转变情况如下
当浮点数小数点后面没有数字时const num1 = 1. // 解析为整数1
当浮点数小数点后面数字只有零时const num1 = 1. // 解析为整数1

2.2、浮点数精确度问题
关于浮点数计算会产生误差的问题,这是一种通病,因为在对使用二进制表示的浮点数进行计算会有可能会产生位数溢出,而又因为js 中浮点数精确度是最高17位,那就会在第十八位进行四舍五入。比如0.1 + 0.2 = 0.30000000000000004,因此0.1 + 0.2 === 0.3 // false

2.3、parseFloat()方法
它规则与parseInt()方法类似,只是它没有第二个参数来表示进制的基数。
2.3.1、如果 parseFloat 在解析过程中遇到了正号(+)、负号(- )、数字(0-9)、小数点(.)、或者科学记数法中的指数(e 或 E)以外的字符,则它会忽略该字符以及之后的所有字符,返回当前已经解析到的浮点数。
2.3.2、第二个小数点的出现也会使解析停止(在这之前的字符都会被解析)。
2.3.3、参数首位和末位的空白符会被忽略。
2.3.4、如果参数字符串的第一个字符不能被解析成为数字,则 parseFloat 返回 NaN

3、NaN
NaN(非数值),表示一个本来要返回数值的操作数未返回数值的情况

3.1、关于NaN的任何操作,返回值为NaN,比如: console.log(NaN/10) // NaN
3.2、NaN与任何值都不相等,包括本身NaN,比如:console.log(NaN === NaN) // false
3.3、isNaN()方法
定义:函数用来确定一个值是否为NaN 。
注意点:对于传入的参数值,它会先检测它是否可以被转化为Number,若是不能则返回NaN。比如:

console.log(isNaN(1)) // false Number(1)为1
console.log(isNaN('')) // false Number('')为0
console.log(isNaN('1')) // false Number('1')为1
console.log(isNaN('blue')) // true Number('bule')为NaN
console.log(isNaN(true)) // false Number(true)为1
console.log(isNaN(null)) // false Number(null)为0
console.log(isNaN(undefined)) // true Number(undefined)为NaN
console.log(isNaN({})) // true Number({})为NaN
console.log(isNaN([])) // false Number([])为0
console.log(isNaN(new Function())) // true

4、Number()方法
为什么把他拎出来说呢?因为在后面操作符,关于数值类型转换,内置使用便是它,而不是parseInt()方法。Number()注意特点
4.1、布尔值,true为1,false为0
4.2、数字值,简单传入传出
4.3、null为0
4.4、undefined为NaN
4.5、字符串
4.5.1、当字面量(允许数字字符后存在一个小数点)为有效的整数(包含十六进制)、浮点数、空字符串,则返回相对应数值

console.log(Number('11')) // 11
console.log(Number('11.')) // 11
console.log(Number('11.11')) // 11.11
console.log(Number('0xff')) // 255
console.log(Number('')) // 0

4.5.2、若还包含其他字符,都返回NaN

console.log(Number('11.11.11')) // NaN
console.log(Number('11.A')) // NaN
console.log(Number('A11')) // NaN
console.log(Number('0xfg')) // NaN

4.6、对象
先调用valueOf()函数,若value不为NaN,则执行Number(value);若value为NaN,则执行toString转化为字符串,再按照字符串规则返回数据

const obj1 = {
  valueOf: () => '111'
}
const obj2 = {}
const arr = [1, 2]
const fun = () => '111'
console.log(Number(obj1)) // 111
console.log(Number(obj2)) // NaN toString为'[object, Object]'
console.log(Number(arr)) // NaN toString为'1,2'
console.log(Number(fun)) // NaN toString为'() => '111''

所以当数组为空数组时

console.log(Number([])) // 0 toString为''

七、String类型
String类型用于表示由零或多个16位的Unicode字符组成的字符序列,即字符串。

1、字符字面量
1.1、关于字符字面量中也包含些特殊的字符字面量,也叫做转义序列,在String.prototype.length中表示一个字符。比如:\n 换行

1.2、还存在某些字符是32位的Unicode字符,比如console.log('?'.length) // 2 因为?的unicode编码为'\ud842\udfb7'
解决方法:我们可以利用正则中的u修饰符,处理unicode码值大于16的情况,知识来源

const compute = str =>{
  const result = str.match(/[\s\S]/ug) 
  // 注意一定要用[]包起,表示匹配一个字符可能为空白字符,也可能不为空白字符;
  // 若为/\s\S/ug,则表示匹配第一个字符为空白字符第二个为非空白字符的字符段
  return result ? result.length : 0
}

console.log(compute('abc')) // 3
console.log(compute('?')) // 1

2、字符串特点
2.1、ECMAScript中的字符串是不可变的,也就是说,字符串一旦创建,它们的值就不能改变。要改变就只能销毁原先的字符串,在将包含新值得字符串填充该变量,这也就是早期为什么拼接字符串较为缓慢。

2.2、内置有Interator接口,可以使用for...in进行遍历元素,同样也可以使用扩展运算符使其字符串对象化或数组化。

3、转化字符串
3.1、toString()方法
在所有的数据类型中,只有Undefined、Null没有.toString()方法,而在存在.toString()方法之中的数据类型中,只有针对Number类型,toString(arg1)中的参数才有效,表示为需转化数值的进制。比如

const fun = new Function()
const num = 11
const sys = Symbol.for('sys')
console.log({}.toString(1)) // [object Object]
console.log([1, 2].toString()) // 1,2
console.log(fun.toString(1)) // function anonymous() { }
console.log(num.toString(8)) // 13
console.log(sys.toString(1)) // Symbol(sys)
console.log('11'.toString(1)) // 11
console.log(true.toString(1)) // true

3.2、String()方法
相比toString()方法的优势是可以使undefined和null转化为字符串,缺点是对于字面量为Number类型的无法通过基数参数转化为相对应进制的字符串。

八、Object类型
ECMAScript中的对象就是一组数据和方法的集合,可能为键值对,可能为数组形式,同样也可能为函数形式。
关于其中的一中值类型,Array是内置有Iterator接口的。详细讲解,第五章将会解析。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值