目录
定义变量关键词
var:
- 可以被重复声明
- 有函数作用域和全局作用域
- 会提前声明,值为undefind
- 可以挂载在window上
let
- 不可以重复声明
- 不可以预解析,必须定义后才能赋值
- 有函数作用域,块作用域和全局作用域
- 实现方式:通过立即执行函数,模拟块作用域
(function(){ var c =3 console.log(c) })() console.log(c) //全局拿不到c的值
const
- 不能重新复值
- 定义时就必须赋值,对于基本数据类型的值,修改值时会报错。但是如果修改引用类型数据时,只会检查数据存放的指针,不会具体检查它的属性。
- 实现方式:
// 实现const function __const( key,value){ // 判断window对象身上是否有当前属性 if(window.hasOwnProperty(key)){ throw new Error("不能重复声明") } if(!value){ throw new Error("声明时请赋值") } window[key]=value // 通过Object.defineProperty监听数据变化 Object.defineProperty(window,key,{ get(){ return value }, set(newvalue){ // 修改时不让修改 if(newvalue!=value) throw new Error("不能重新赋值") } }) } __const("text",1) console.log(text)
基本数据类型
number
- 十进制数字
- 浮点数
- 其他进制数字
- 科学计数 2e^-1
- NaN 非数字 记住NaN!=NaN,如果需要判断使用Number.isNaN() / isNaN()来判断。两者区别在于,Number.isNaN(data)要同时满足data类型为number,并且值为NaN。而isNaN会将类型转成number,然后在判断是否为NaN
string: 字符串
boolean: 布尔类型,值为true和false
undefined: 已定义但未赋值。
null:对象类型初始时手动赋值为null
symbol: 独一无二的值,不会重复。
bigint:超大整数最大值为2的53次方-1
null 与 undefined 区别: 作者在设计时参考Java设计了null,但是null为对象并且会自动隐式转成0,于是设计了undefined。
面试题:
精度丢失:0.1+0.2=0.300004
原因:因为在计算机最底层,数值的运算和操作都是采用二进制实现的,所以计算机没有办法精确表示浮点数,而只能用二进制近似相等的去表示浮点数的小数部分。
解决方案:先化整计算,在相除(1+2)/10
引用类型
object:对象常用方法
- Object.keys(),可以用来判断是否是空对象
- Object.values()
- Object.entries()
- Object.assign() // 可以实现浅拷贝
- Object,prototype.toString().call()
- 解构赋值,拓展运算符
- 注意对象的key只能是string类型,如果你设置成key为对象的话会隐式转为字符串 例如:{a:1} => '[object Object]' ,这个与Map类型不同。
function:常用方法改变this的指向
- call(this的指向,c1,c2)
- appy(this的指向,[参数])
- bind(this的指向,c1,c2)不同于上两个,call和apply方法都是在调用之后立即执行的。而bind调用之后是返回原函数,需要再调用一次才行。
arry:常用方法
- push:向尾部添加一个或多个数据
- pop:删除最后一个数据
- unshift:向头部一个或多个数据
- shift:删除头部第一个数据
- splice:三个参数 ,开始索引,删除的数目,插入的新数据(从开始索引的位置)
- join:数组转字符串
- sort:数组正序排序 // 之前原理是通过快排和插入排序,现在是冒泡
let arr=[1,3,0,3,6,8,3,7] let res=arr.sort((a,b)=>{ console.log("a="+a,"b="+b) return a-b//正序 //return b-a //逆序 }) console.log(res,"res") //类似数据结构的插入排序法
- reverse:反向排序
- includes:判断是否存在某个字符
- indexOf:同上,但是返回值不同
- 解构赋值,拓展运算符
一些常用遍历方法
- filter:过滤符合条件的数据
- map:更改每项数据结构
- reduce:一般用于计数,计算总价,数量等
- some:遍历多次有一次满足返回值条件,整体返回值为true
- every: 整体满足条件返回true
- find:找到数组中第一个满足返回值条件的数据
// 实现map Array.prototype.Map = function(callback){ // this为数组实例 const len = this.length; let arr = [] for(let i= 0 ; i< len; i++){ arr[i] = callback(this[i] , i , this) } return arr; }
字符串常用方法
- splite:将字符串转成数组,处理数据常用
- indexOf:查看是否含有指定内容
- replace:替换字符,一般结合正则表达式
- trim:去除两端空格
- slice:截取指定内容,不会影响原字符串
基本数据类型和引用数据类型区别
两者作为函数的参数进行传递时:
- 基本数据类型传入的是数据的副本,原数据的更改不会影响传入后的数据。
- 引用数据类型传入的是数据的引用地址,原数据的更改会影响传入后的数据。
两者在内存中的存储位置:
- 基本数据类型存储在栈中。
- 引用数据类型在栈中存储了指针,该指针指向的数据实体存储在堆中。
数据类型的判断
- 基础类型使用typeof
判断引用类型Object.prototype.toString.call() 通过构造器 constructor.name 拿到构造器名称 instance of :A instanceof B 可以用来判断 A 是否为 B 的实例,但它不能检测 null 和 undefined ;
浅拷贝和深度拷贝
浅拷贝只复制指向某个对象的指针,而不复制对象本身。浅拷贝的实现方式有:
- Object.assign():需注意的是目标对象只有一层的时候,是深拷贝;
- 扩展运算符;
深拷贝就是在拷贝数据的时候,将数据的所有引用结构都拷贝一份。深拷贝的实现方式有:
- 手写遍历递归赋值;
- 结合使用JSON.parse()和JSON.stringify()方法。 // 但是这个要注意JSON 不支持 undefined ,函数等类型。
- 使用lodash中_.cloneDeep()方法
函数知识点
普通函数:
- this的指向:全局作用域中的函数:其内部this指向window;对象内部的函数:其内部this指向对象本身; 构造函数:其内部this指向生成的实例;由apply、call、bind改造的函数:其this指向第一个参数;
箭头函数
箭头函数this指向:箭头函数没有自己的 this ,看其外层的是否有函数,如果有,外层函数的 this 就是内部 箭头函数的this ,如果没有,则 this 是 window 。 箭头函数不能做构造函数,生成实例。立即执行函数
- 一般用于解决闭包问题
- (fuction(){//执行语句})()
函数闭包
- 闭包:定义在一个函数内部的函数。其中一个内部函数 在包含它们的外部函数之外被调用时,就会形成闭包。
- 闭包用途:
1 、读取函数内部的变量2 、让这些变量的值始终保持在内存中。不会再 f1 调用后被自动清除。3 、方便调用上下文的局部变量。利于代码封装。原因: f1 是 f2 的父函数, f2 被赋给了一个全局变量, f2 始终存在内存中, f2 的存在依赖 f1 ,因此 f1 也始终存在内存中,不会在调用结束后,被垃圾回收机制回收。 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网 页的性能问题,在IE 中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除,或者通过立即执行函数。
作用域
全局作用域:
预编译
- (针对于var)先生成Go对象
- 查找var声明的变量,赋值为undefind
- 查找函数声明,进行赋值
- 按照代码执行的顺序,再次赋值
函数作用域:
预编译:
- (也针对于var)创建Ao对象
- 查找形参和变量声明,赋值为undefind
- 将实参和形参统一,进行赋值
- 在函数中找函数声明,重新赋值
- 函数执行,对未执行代码进行重新赋值
块作用域:let,const声明的变量独有,能够在{}块中保留变量当前的值。
- 每个函数执行都会产生一个作用域链,当函数需要查找某个变量时,会在自身的ao对象中查找,如果找不到在上一个函数的ao继续找,直到找到全局go对象上,如果还没有就undefined.
- 当一个函数中声明了其他函数,在其他函数产生的AO和外部函数的AO指向同一个地址,如果在其他函数内部修改了外部的某个值,外部函数的Ao值同样会发生变化。
原型和原型链
- 原型的概念 :JavaScript的象中都包含了一个 [proto] 内部属性,这个属性所对应的就是自身的原型
- 原型链的概念 :当一个对象调用自身不存在的属性/方法时,就会去自己 [proto] 关联的前辈 prototype 对象上去找,如 果没找到,就会去该 prototype 原型 [proto] 关联的前辈 prototype 去找。依次类推,直到找到属性/方法或 undefined 为止。从而形成了所谓的“原型链”。
继承方式
- class+extends
//类模板 class Animal { constructor(name){ this.name = name } } //继承类 class Cat extends Animal{ //重点 extends方法,内部用constructor+super constructor(name) { super(name); //super作为函数调用时,代表父类的构造函数 } //constructor可省略 eat(){ console.log("eating") } }
- .原型继承
//类模板 function Animal(name) { this.name = name; } //添加原型方法 Animal.prototype.eat = function(){ console.log("eating") } function Cat(furColor){ this.color = color ; }; //继承类 Cat.prototype = new Animal() //重点:子实例的原型等于父类的实例
- 通过构造函数
function Animal(name){ this.name = name } function Cat(){ Animal.call(this,"CatName") //重点,调用父类的call方法 }