JS里的数据

背景

  1. 1991年,李爵士发明 www
  2. 1992年,同事发明 CSS
  3. 1993年,创建 W3C
  4. 1995年,网景浏览器 Netscape —> Navigator 脚本
  5. Branden Eich JS 之父,推出 Mocha 脚本语言,看起来像 Java
  6. 语言的转变 Mocha —> LiveScript —> JavaScript

  7. 这个时候 Unicode UTF-8 发布,这个时候 JS 已经有 bug 了,主要在于编码方面的

  8. 1996年, MS IE 想要抢占浏览器市场,发明 JScript

  9. ECMAScript (申报标准)<—-网景开源 —-> firefox
  10. IE5.5 MS推出 JS发请求
  11. 2004年, Gmail 网页上的程序
  12. 后端—>前端 Front-end(以 JS 为生)
  13. 2010年左右,中国开始逐渐有前端开发这类职位

然而,JS 这个时候

  1. 全局变量(没有模块化)
  2. 标准化(内置代码少)

这个时候 ECMAScript 进行一次代码升级 从 ES3 —> ES5(ES4 死了),但是步子太小
Rails 社区 Ruby —> CoffeeScript JS 改良版
添加了 类、箭头函数、opiond chain 语法
ECMAScript6 步子合适,JS 终于成为一个现代编程语言

数据类型

JS 一共有 7 种数据类型

  • 数字/字符串/布尔/symbol(符号)/null/undefined/对象
  • 即 number/string/boolean/symbol/null/undefined/object(array,function…)
  • 所以你看,”JS 一切皆对象” 这句话严格意义上来说是错的

number

十进制

1      // 1
1.1 // 1.1
.1 // 0.1
1.23e2 // 123

二进制(以 0b 开头)

0b11 // 3

八进制(0 开头)

011 // 9

十六进制(0x 开头)

0x11 // 17

字符串(String)

'你好' "你好" '' ""(空字符串)

转义符号 “\” 用于多行字符串(不是字符串里有回车)

var s = '12345 \            // 坑人语法
67890'
var s2 = '12345' + //好读的语法
'67890'

var s3 = '12345 ' (这里有空格) // 而这里有报错
'67890'

因为上面,所以 ES 6 做了升级处理,如下

// 注意这里是反引号
// 这里也是包含有回车的
var s4 = ` 12345
67890`

布尔(boolean)

  • true
  • false
  • && 与
  • || 或

symbol

symbol 来源

symbol 就是那个对象全局的唯一的值,这其实很像数据库中的唯一识别 id,这个唯一识别的 id 对应一条完整的数据
而这个是用来干嘛的,简单来说就是这个值是用来标识一个对象的属性的,好了,为什么需要标识这个对象的属性呢?
想像一下你需要用别人提供的一个对象,然后你想在这个对象里面加个方法,但是假设你加的方法名跟对象里面的方法名一样,就会对原有对象方法进行了
修改,这样就污染了原对象。那么能不能有一种机制来起到报警/提示/避免的作用呢? 有,ES6 就引入了这种新的原始数据类型叫 symbol

理解 symbol

引入 symbol 前

class person {
constructor(options) {
this.name = options.name;
this.age = options.age;
}
getAge() {
return this.age;
}
}

let xxx = new person({
name: 'xxx',
age: 1
});
xxx.getAge() // 1

const GetAge = { // mixin
getAge() { console.log('xxx'); }
}
// 这里就修改了原对象方法
Object.assign(person.prototype, GetAge);
xxx.getAge() // 'xxx'

引入 symbol 后

let getAge = Symbol();
class person {
constructor(options) {
this.name = options.name;
this.age = options.age;
}
[getAge]() {
return this.age;
}
}

let xxx = new person({
name: 'xxx',
age: 1
});
xxx[getAge]() // 1

// let getAge = Symbol(); // 报错 重复定义
getAge = Symbol();
const GetAge = { // mixin
[getAge]() { console.log('xxx'); }
}
// getAge 是一个新的 Symbol 值,所以这里就没有覆盖掉以前的
Object.assign(person.prototype, GetAge);
xxx[getAge]() // 'xxx'

symbol 实例

假如有一个函数是这样的

function test(type) {
switch(type) {
case 'xxx':
console.log('1');
break;
}
}

现在调用它

var demo = {
xxx: 'xxx',
yyy: 'yyy'
}
test('xxx'); // '1'
// 或者
test(demo.xxx); // '1'

但是这么做不好,因为字符串可能有些有拼写错误会造成一些 bug,可以这么改

function test(type) {
switch(type) {
case type.xxx:
console.log('1');
break;
}
}
var demo = {
xxx: 'xxx',
yyy: 'yyy'
}
test(demo.xxx); // '1'

但是我们发现,此时不管 demo.xxx 里的值为何值它都是会执行 console.log('1')

demo.xxx = 'demo';
test(demo.xxx); // '1'

只要不冲突,那干什么都行的话,那为什么不用 Symbol ???

var demo = {
xxx: Symbol(),
yyy: Symbol()
}
test(demo.xxx); // '1'

另外需要强调的是,Symbol 可以创建一个独一无二的值,但它并不是字符串!!!
也可以给 Symbol 传参,但是这个只是起到一个’注释’的作用,如下

var a1 = Symbol('a');
var a2 = Symbol('a');
a1 === a2; // false
console.log(a1); // Symbol(a)
console.log(a2); // Symbol(a)

null 与 undefined

如果一个值为 null 或者 undefined 则它都表示什么也没有,这也是 JS 的原创 bug

区别:

i.(规范)如果一个变量没有被赋值,那么这个变量的值就是 undefined

ii.(大家都认为)如果你想表示一个没有赋值的 String/Number/Boolean/Symbol,就用 undefined

iii.(大家都认为)如果你想表示一个没有赋值的对象,就用 null

object

前面说的都是基本类型或者说是简单类型,而 object 是复杂类型,但是所谓的复杂类型也是由简单类型组成的

var name = 'frank'
var age = 18
var gender = 'male'

var person = {
'name': 'frank',
'age': 18,
'gender': 'male'
}

语法
// 声明定义
var person = {
name: 'frank',
age: '18',
self: person,
'': 'frank', // 这个是可以成立的
9a: 'frank', // 这样写不行
'9a': 'frank', // 这样是行的
'a b': 'frank', // 这样也是可以的
'中文': 'frank',// 中文是可以的
// 如果不加引号,那么就 按照标识符来判断是否符合语法
}
// 调用
person['name']
// 下面的语法成立么
person.self.self.self.self.name
// 调用空字符串成立
person['']

其他操作符

typeof

xxx 的类型 string number boolean null undefined symbol object function
typeof xxx ‘string’ ‘number’ ‘boolean’ ‘object’ ‘undefined’ ‘symbol’ ‘object’ ‘function’

delete

delete 操作符用来删除对象的某个属性,若没有指向这个属性的引用,那它最终会被释放

var Employee = {
age: 28,
name: 'abc',
designation: 'developer'
}

console.log(delete Employee.name); // returns true
console.log(delete Employee.age); // returns true

// 当试着删除一个不存在的属性时
// 同样会返回true
console.log(delete Employee.salary); // returns true

in

若指定的属性在其指定的对象或原型链中,则 in 运算符返回 true

// 数组
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
0 in trees // 返回true
3 in trees // 返回true
6 in trees // 返回false
"bay" in trees // 返回false (必须使用索引号,而不是数组元素的值)

"length" in trees // 返回true (length是一个数组属性)

Symbol.iterator in trees // 返回true (数组可迭代,只在ES2015+上有效)

// 内置对象
"PI" in Math // 返回true

// 自定义对象
var mycar = {make: "Honda", model: "Accord", year: 1998};
"make" in mycar // 返回true
"model" in mycar // 返回true

for in

for…in 语句可以以任意顺序遍历一个对象的可枚举属性,对于每个不同的属性,语句都会执行

var obj = {a:1, b:2, c:3};

for (var prop in obj) {
console.log("obj." + prop + " = " + obj[prop]);
}

// Output:
// "obj.a = 1"
// "obj.b = 2"
// "obj.c = 3"

for of

for…of 语句在可迭代的对象 (array, string, map, set…) 上创建一个迭代循环,也是为不同的属性值执行语句

let iterable = [10, 20, 30];

for (let value of iterable) {
value += 1;
console.log(value);
}
// 11
// 21
// 31

for in 和 for of 的区别

首先,上代码!

Object.prototype.objCustom = function() {}; 
Array.prototype.arrCustom = function() {};

let iterable = [3, 5, 7];
iterable.foo = 'hello';

for (let i in iterable) {
console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom"
}

for (let i in iterable) {
if (iterable.hasOwnProperty(i)) {
console.log(i); // logs 0, 1, 2, "foo"
}
}

for (let i of iterable) {
console.log(i); // logs 3, 5, 7
}

上面代码我们可以看到区别了

  • ‘hello’ 3 5 7 这些是属性对应的值,它不是可枚举属性
    -for in 在遍历对象时,若不加条件,它会一直沿着原型链遍历这个对象新加的可枚举属性
    -for of 在遍历对象时,它会遍历该对象定义的迭代值,而不是任何对象的属性

参考链接

知乎 JS 中的 Symbol 是什么?
阮一峰 Symbol