JS的数据类型

205 阅读10分钟

Unicode:收录十三万字符(大于16)位,全球通用。但体积过大,性价比不高。

UTF-8:用Unicode,采用变长字节存储方法。

布尔boolean

  • 取值:true/false
console.log(1===2)//false
console.log(1===1)//true

5个falsy值

  • 相当于false但又不是false的值
  1. undifined
  2. null
  3. NaN
  4. 0
  5. ''

⭐只有false和5个falsy值是假值,其他的都是真值。

undifined和null

  • 二者没有本质区别
  • 如果一个变量声明但没有赋值,默认值是undifined
  • 如果一个函数没有写return,那么默认return undefined.

数字number

写法

  1. 整数写法:1
  2. 小数写法:0.1
  3. 科学计数法:1.23e4
  4. 八进制写法:0123
  5. 十六进制写法:0x3F
  6. 二进制写法:0b11

特殊值

  • 正0和负0
  • 无穷大:Infinity,-Infinity,+Infiniyt
  • 无法表示的数字:NaN

JS数字的存储形式:64位浮点数

  • 符号1位
  • 指数11位
  • 有效数字52位

范围和精度

  • 范围:Number.MIN_VALUE ~ Number.MAX_VALUE
  • 精度(有效数字):最多只能到52+1个二进制位表示的有效数字

字符串string

  • 每个字符两个字节

写法

  • 'hello'
  • "hello"
  • hello(``反引号中可以直接打回车) ⭐引号不属于字符串的一部分,如果要包含引号,需要在字符串内的引号前加\。或者用与字符串转不同的引号包裹这个字符串

属性

  • string.length:字符串的长度
  • 通过下标访问字符串中的字符:
var string = "abcdreasd"
console.log(string[0]===a);//true

base64转码

  • window.btoa:正常字符串转换为Base64编码的字符串
  • window.atob:编码的字符串转换为原来的字符串

符号symbol

  • Symbol()函数会返回symbol类型的值,该类型具有静态属性和静态方法。
  • 每个从Symbol()返回的symbol值都是唯一的。一个symbol值能作为对象属性的标识符;这是该数据类型仅有的目的。
const symbol1 = Symbol();
const symbol2 = Symbol(42);
const symbol3 = Symbol('foo');
console.log(typeof symbol1);
// expected output: "symbol"
console.log(symbol2 === 42);
// expected output: false
console.log(symbol3.toString());
// expected output: "Symbol(foo)"
console.log(Symbol('foo') === Symbol('foo'));
// expected output: false

变量声明:同时

同时指定值和类型,但值和类型都可以改。

  • var
  • let:区块变量,只作用于它存在的那一块代码块。不可以在同一个块重复声明,赋值与否都可以。必须先声明再使用。
  • const:跟let规则差不多,通常设置为常量。声明时就要赋值,赋值后就不能。

⭐对象object

定义

无序的数据集合/键值对的集合

写法

  • 键名是字符串,不是标识符,可以包含任意字符
  • 引号可以省略,省略之后就只能写标识符(⭐就算省略引号,key还是字符串,不管输入什么类型,都会变成字符串)
  • Object.keys(obj):可以得到所有的key
let obj = {'name':'fumo','age':18}//简单写法
let obj = new Object({'name':'fumo','age':18})
console.log({'name':'fumo','age':18})//匿名对象
Object.keys(obj)//["name","age"]
/*如果要用变量的值作为key*/
let a = "oneoneone"
let obj2 ={[a]:111}

对象的隐藏属性

  • JS中每个对象都有一个隐藏属性,隐藏属性储存着原型(共有属性组成的对象)的地址

删除属性

  • 删除
let obj = {'name':'fumo','age':18}
obj.name=undifined//删除属性值保留属性名
delete obj.name//删除属性
/*也可以写成*/
delete obj['name']
console.log('name' in obj)//false

增加/修改属性

  • 直接赋值:
let obj = {"name":"fumo"}
obj.name = 'fumo'
obj['name'] = fumo
  • 批量赋值:
let obj = {"name":"fumo"}
Object.assign(obj,{'age':18,'gender':'woman'})
  • 修改或增加共有属性
    • 无法通过自身修改或增加共有属
    • 非要修改的话:obj.proto.toSring='xxx'/Object.prototype.toString='xxx'
  • 修改隐藏属性
      let obj1 = {name:'fo'}
      let obj2 = {name:'mo'}
      let common = {kind:'human'}
      /*修改隐藏属性*/
      let obj1 = Object.create(common)//推荐,以common为原型创建obj1的意思
      obj2.__proto__=common//不推荐,相当于变成原型链。
    

读属性

  • 读取对象中的属性
  • ⭐'name' in obj和obj.hasOwnProperty('name') 的区别:in会查看隐藏属性中的属性,hasOwnPropetry不会查隐藏属性中的属性。
let obj = {'name':'fumo','age':18}
Object.keys(obj)//["name","age"],读取所有的key
Object['name']//fumo
Object.name//fumo
Object.values(obj)//["fumo",18],读取所有的value
Object.entries(obj)//返回键值对,无隐藏属性
console.log(obj)//打印出所有属性,以及隐藏属性。
obj.hasOwnPropetry("toString")//判断对象的方法是否是自己独有的

构造函数

  • 可以构造出对象的函数叫构造函数
  • 把细节都写在一个函数中,然后调用函数达到效果。这叫封装。
    function Square(width){ 
      this.width = width
    }
    Square.prototype.getArea = function(){ 
      return this.width * this.width 
    }
    Square.prototype.getLength = function(){
      return this.width * 4
    }
    let square = new Square(5)
    
    • new X():X为构造函数,负责给构造对象添加自身属性。X.prototype负责保存对象的共用属性。new会自动创建空对象,空对象自动关联原型,自动将空对象作为this关键字运行构造函数,自动return this.
    • 所有js中的函数从出生就自带一个prototype的属性,prototype从出生就自带一个constructor,constructor从出生时它的值就是函数自身。

关于 prototype 属性

  1. 所有函数一出生就有一个 prototype 属性
  2. 所有 prototype 一出生就有一个 constructor 属性
  3. 所有 constructor 属性一出生就保存了对应的函数的地址
  4. 如果一个函数不是构造函数,它依然拥有 prototype 属性,只不过这个属性暂时没什么用

原型

  • 每个对象都有原型,原型存着对象的共有属性,比如obj.__proto__存着原型的地址。
  • 对象的原型也是对象,原型包含所有对象的共有属性,是对象的根。原型的__proto__(原型)是null。
  • 如何确定一个对象的原型?:谁构造的,原型就是谁的prototype属性(对象.proto === 构造函数.prototype)。此公式不适用于非构造函数构造出的对象(Object)

类型VS类

  • 类型:是JS数据的分类,有七种,数字、字符串、布尔、符号Symbol、null、undefined、对象。
  • 类:针对于对象的分类,有无数种。常见有Aray,Function,Date等等。

数组对象

  • js的数组元素类型可以不同,且内存储存不一定事连续的。不可以通过数字下标访问,只能用字符串下标。
  • 定义一个数组
    let arr =[1,2,3]
    let arr2 = new Array(1,2,3)//元素为1,2,3
    let arr3 = new Array(3)//长度为三的空数组
    let arr = "1,2,3".split(',')//以,为分隔符将字符串分成数组。
    Array.form(0:'a',1:'b',2:'c',length:)//新标准,把非数组变为数组,一般需要有下标和长度两个属性
    
  • 数组对象的自身属性:数组内元素和数组长度length
let arr = [1,2,3]
let count = arr.length;//数组长度
/*push*/
count = arr.push(4,5,6);//添加一个或多个元素到末尾并且返回数组新长度。
console.log(arr)//[1,2,3,4,5,6]
/*pop*/
console.log(arr.pop())//6;删除数组的最后一个元素,并返回该元素的值。
/*shift*/
count = arr.shift()//删除数组的第一个元素,返回数组新长度。
console.log(arr)//[2,3,4,5]
/*unshift*/
count = arr.unshift(1,1,1)//将多个元素添加到数组的开头,并返回数组新长度。
console.log(arr)//[1,1,1,2,3,4,5]
/*join*/
let strings = ['I','LOVE','YOU']
console.log(strings.join())//"I,LOVE,YOU" 
//join() 方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符。
/*splice*/
let d = [1,2,3,4,5]
d.splice(2,2)//从下标为2的地方开始删除2个元素。[1,2,5]
d.splice(2,1,666,777)//从下标为2的地方删除一个元素,并添加元素666,777在数组中。[1,2,666,777]
d.splice(2,0,23,24)//从下标为2的地方删除0个元素,增加23,24,[1,2,23,24,666,777]
/*修改数组*/
d.reverse()//反转数组,修改原数组
d.sort()//给乱序数组排序,默认从小到大
let f = [
  {name:'a',score:99},{name:'b',score:95},{name:'c',score:100}
]
f.sort((a,b)=>a.score-b.score)//数组元素为对象时,要用其中某个属性排序。
/*concat*/
let a = [1,1,1]
let b = [2,2,2]
let c = a.concat(b)//c:[1,1,1,2,2,2],返回一个新数组,不改变原来的数组
/*slice*/
console.log(c.slice(2))//从2开始取。切片[1,2,2,2],取0的时候一般用作数组拷贝(浅拷贝)
/*访问数组的属性名和值*/
/*所有属性*/
//一般采用for循环(非for in)
let e = [1,2,3,4,5]
for(let i = 0;i<e.length;i++){
  console.log('${i}:${e[i]}')//0:1....
}
//或者
arr.forEach( function(xxx,yyy,zzz){
  console.log('${yyy}:${xxx},${zzz}')//0:1 [1,2,3,4,5]
})
//两者区别:forEach不支持break和continue。for循环是代码块,forEach是函数块
/*访问单个元素*/
console.log(e.indexOf(5))//4,存在返回下标,不存在返回-1
e.find(item =>item%2===0)//返回数组中第一个满足条件的元素值
e.findIndex(item =>item%2===0)//返回数组中第一个满足条件的元素下标
  • 伪数组:原型链中没有数组的原型
  • ⭐delete删除数组的一个元素后,数组长度不会改变,对应内容用empty代替。
  • ⭐直接更改数组长度会导致数组元素被删除。
  • 数组变化
    let arr = [1,2,3,4,5,6]
    //以下只返回新数组,不改变原数组
    /*map---n变n,一一映射*/
     console.log(arr.map(item => item*item))//[1,4,9,16,25,36]
    /*filter---n变少*/
     arr.filter(item => item%2===0)//[2,4,6]
    /*reduce---n变1*/
    /*
    arr.reduce((sum,item)=>{return sum+item},结果初始值)
    return的值作为下一次的结果初始值
    */
    arr.reduce((sum,item)=>{return item+sum},0)//21
    arr.reduce((result,item)=>{return result.concat(item*item)},[])//[1,4,9,16,25,36]等价于上面的map方法。
    arr.reduce((result,item)=>{return item%2===1?:result.concat(item)},[])//[2,4,6]等价于上面的filter
    arr.reduce((result,item)=>{return item%2===0?:result.concat(item)},[])//[1,3,5]
    /*reduce例题--将数组变换为对象*/
    let arrs = [
      {name:'动物',id:1,parent:null},
      {name:'猫',id:2,parent:1},
      {name:'狗',id:3,parent:1}
    ]
    arr.reduce((result,item)=>{
      if(item.parent===null){
        result.id = item.id
        result.name = item.name
      }else{
        result.children.push(item)
        delete item.children
        item.parent = null
      }
      return result
    },{id:null,name:null,children:[]})
    

函数对象

  • 定义一个函数
function fn(x,y){return x+y}
let fn2 = function fn(x,y){return x+y}//把fn的地址给a,只能通过a来调动函数。
let fn = (x,y) => x+y//箭头函数
let fn = new Function('x','y','return x+y')//构造函数
  • 函数的执行时机-------见另一篇笔记
  • 作用域:全局变量(最顶层变量/window变量)vs局部变量(代码块中)---嵌套函数中,作用域采用就近原则
  • 闭包:闭包就是能够读取其他函数内部变量的函数
    • 闭包的作用:连接函数内外部。
  • 形式参数:非实际参数,是调用函数时传递真实参数的媒介
  • 返回值:每个函数都有返回值。不写return会返回undefined。函数执行完毕才会返回。且只有函数有返回值。
  • 调用栈:解释器追踪函数执行流的一种机制。当执行环境中调用了多个函数时,通过这种机制,我们能够追踪到哪个函数正在执行,执行的函数体中又调用了哪个函数。 ... 当前函数执行完毕后,解释器将其清出调用栈,继续执行当前执行环境下的剩余的代码
  • 函数提升:不管函数声明在哪里,都会跑到第一行去
  • arguments(除了箭头函数):伪数组
  • this(除了箭头函数):如果不给任何条件,this默认指向window。如果不加'use strick',this会尽量把传递过去的值变成对象。
  • 箭头函数:无this和arguments。箭头函数里面的this就是外面的this。把this当成了普通的变量使用