这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
常见问题
静态成员只能通过构造函数访问,不能通过实例对象访问
栈:全局变量/局部变量
堆:对象
闭包:嵌套的内部函数,且引用了外部函数的数据
function fn(){
var a = 1
function fn2(){ //执行函数就会产生闭包(不用调用内部函数)
a++
}
return f
}
变量提升&函数提升
理解一:函数提升要比变量提升的优先级要高一些,且不会被变量声明覆盖,但是会被变量赋值之后覆盖。
理解二:变量提升先声明值为undefined,然后函数提升覆盖为function,然后变量复制覆盖为number
function c(c) {
console.log(c)
}
var c = 1
c(2) //c is not a function
var c = 1
function c(c) {
console.log(c)
}
c(2) //c is not a function
var c = 1
c(2) //c is not a function
function c(c) {
console.log(c)
}
function c(c) {
console.log(c)
}
c(2) //2 调用在赋值之前,此时c还是function
var c = 1
数据类型判断
typeof:
可以判断: undefined/ 数值 / 字符串 / 布尔值 / function
不能判断: null与object object与array
instanceof:
判断对象的具体类型
可以判断: undefined, null
undefined 代表定义未赋值
null 定义并赋值了, 只是值为 null
var b = null // 初始赋值为null, 表明将要赋值为对象
['属性名']
var p = {
a : "111",
content-b: "222"
}
p.a === p["a"]
但是p.content-b会报错,必须用p["content-b"]
变量名不确定时
var propName = "c"
p.propName = "333" //使用错误
p[propName] = "333" //必须使用这个
原型
原型是一个对象
作用:共享方法(一般情况下,公共属性放构造方法里,公共方法放原型对象里)
function Son(name,age){ //构造函数
this.name = name //this指向son
this.age = age
}
Son.prototype.say = function(){ //此原型对象里this也指向son
console.log("0000")
}
var son = new Son()
console.dir(son.__proto__ === Son.prototype) //true
//实例对象son上会自动添加一个——proto——属性,指向函数对象的prototype原型对象,如上
//方法查找: son先看自身对象上有没有say方法,如果没有,因为有——proto——,所以能去函数对象的prototype上找,如果还是没有,接着往上找——proto——
//constructor构造函数 指向构造函数本身,用来查看当前对象是由哪个函数创建的
各种方法
改变this指向
call()
function fn(x,y){
console.log(this) //this指向window
console.log(x+y)
}
var o = {
name="000"
}
fn.call(o,999,999) //this指向o (第一个参数改变this指向,其余的为被调用函数的参数)
apply()
fn.apply( thisArg [,argsArray])
//thisArg 指定this指向
//argsArray 必须是数组
function fn(arr){
console.log(this) //this指向window
console.log(arr) //"999"
}
var o = {
name="000"
}
fn.apply(o,["999"]) //this指向o
应用: 求数组最大值
var arr = [1,4,5,3]
var max = Math.max.apply(Math,arr)
console.log(max) //5
bind()
不会调用该函数,只改变this指向
fn.bind( thisArg, arg1, arg2, ....)
//返回一个修改this指向后的新函数
trim()
返回一个新的字符串
str.trim() //删除两端的空白字符
Object.defineProperty()
添加新属性或修改原来的属性
Object.defineProperty( obj, prop, descriptor )
//obj 原对象
//prop 属性
//descriptor 为对象形式{} 参数说明如下:
value: 设置属性的值默认为 undefined
writable: 值是否可以重写。 true | false 默认为 false
enumerable: 目标属性是否可以被枚举。 true | false 默认为 false(不允许遍历)
configurable: 目标属性是否可以被删除或是否可以再次修改特性 true | false 默认为 false(不允许修改这几个特性)
Object.keys()
获取对象的所有属性
Object.keys( obj ) //效果类似for...in
//返回由所有属性组成的数组
数组遍历方法
forEach()
注意:return不会中止遍历
array.forEach( function(currentValue, index, arr) )
//currentValue:当前项的值
//index:当前项的索引
//arr:数组对象本身
var arr= [1,2,3,4,5,6]
arr.forEach((value,index,array) => {
if(value === 3)
return //b
console.log(value) //1,2,3,4,5,6
console.log(index) //0,1,2,3,4,5
console.log(array) //[1,2,3,4,5,6]
});
filter()
注意:返回一个新数组
array.filter( function(currentValue, index, arr) ) //主要用于筛选数组
//currentValue:当前项的值
//index:当前项的索引
//arr:数组对象本身
var arr= [1,2,3,4,5,6]
var newArr = arr.filter((value,index,array) => {
return value > 3
});
console.log(newArr) //[4,5,6]
some()
注意:返回值是布尔值,如果找到元素返回true,反之false。 若找到第一个满足的元素则运行结束
return 不能中止遍历
array.some( function(currentValue, index, arr) ) //查找数组中是否又满足条件的元素
//currentValue:当前项的值
//index:当前项的索引
//arr:数组对象本身
var arr= [1,2,3,4,5,6]
var flag = arr.some((value) => {
return value = 3
//return value > 3
});
console.log(flag) //true
严格模式
- 先声明再使用
- 不能随意删除已经声明好的变量
- 普通模式下全局函数this指向window,严格模式下全局函数this指向undefined
-
如果构造函数不加new调用 this会报错 function Fn(){ this.name = "000" //this指向undefined } Fn(); - 定时器的this还是指向window
- 函数不能存在同名函数
-
函数声明必须在顶层,不允许在非函数的代码块内声明函数 if(){ function () //报错 } for(){ function () //报错 } function () { function() //可以运行 }
闭包
闭包(closure)指有权访问另一个函数作用域中变量的函数——JavaScript高级程序设计
可以延申变量的作用范围 容易造成内存泄漏(因为一直不销毁变量)
JS浅拷贝、深拷贝
浅拷贝
引用拷贝、地址拷贝
(适用于数组并不复杂,即数组中没有嵌套对象或者嵌套数组)
方法一:concat()
concat()方法用于连接两个或多个数组; concat() 方法不会更改现有数组,而是返回一个新数组,其中包含已连接数组的值。
var arr = [1, 2, 3];
var newArr = arr.concat();
arr.push(4);
console.log(newArr); // [1, 2, 3]
方法二:slice()
slice() 方法以新的数组对象,返回数组中被选中的元素; slice() 方法选择从给定的 start 参数开始的元素,并在给定的 end 参数处结束,但不包括; slice() 方法不会改变原始数组;
var arr = [1, 2, 3];
var newArr = arr.slice();
arr[0] = 10;
console.log(arr);// [10, 2, 3]
console.log(newArr);// [1, 2, 3]
方法三:扩展运算符
var arr = [1, 2, 3];
var [ ...newArr ] = arr;
arr[0] = 10;
console.log(arr); // [10, 2, 3]
console.log(newArr);// [1, 2, 3]
12345
方法四: Object.assign()
var arr = [1, 2, 3];
var newArr = Object.assign([], arr); //把arr拷贝给[]
arr[0] = 10;
console.log(arr);// [10, 2, 3]
console.log(newArr);// [1, 2, 3]
如果数组元素是对象或者数组,上面四种方法就会只拷贝数组或者对象的引用,如果我们对其中一个数组进行修改,另一个数组也会发生变化 比如:
var arr = [ { a: 1 }, [ 1, 2 ], 3 ];
let newArr = arr.concat();
arr[0].a = 2;
console.log(arr); // [ { a: 2 }, [ 1, 2 ], 3 ]
console.log(newArr);// [ { a: 2 }, [ 1, 2 ], 3 ] 值被影响
深拷贝
(可以完全拷贝一个数组,即使嵌套了对象或者数组,两者也不会互相影响)
方法一:JSON.parse(JSON.stringify(arr))
var arr = [ { a: 1 }, [ 1, 2 ], 3 ];
// let newArr = JSON.parse(JSON.stringify(arr));
let newArr = arr.concat();
arr[0].a = 2;
console.log(arr); // [ { a: 2 }, [ 1, 2 ], 3 ]
console.log(newArr);// [ { a: 1 }, [ 1, 2 ], 3 ]
123456
但是该方法是有局限性的
- 会忽略 undefined
- 会忽略 symbol
- 不能序列化函数
- 不能解决循环引用的对象
比如下面这个例子:
let a = {
age: undefined,
sex: Symbol('male'),
jobs: function() {},
name: 'sun'
}
let b = JSON.parse(JSON.stringify(a))
console.log(b) // {name: "sun"}
方法二:通用方法(数组或对象)
拷贝的时候判断属性值的类型,如果是对象,继续递归调用深拷贝函数(简易版)
var deepCopy = function(obj) {
// 判断是否是对象
if (typeof obj !== 'object') return;
// 判断obj类型,根据类型新建一个对象或者数组
var newObj = obj instanceof Array ? [] : {}
// 遍历对象,进行赋值
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
let val = obj[key];
// 判断属性值的类型,如果是对象,递归调用deepCopy
newObj[key] = typeof val === 'object' ? deepCopy(val) : val
}
}
return newObj
}
123456789101112131415
方法三:利用lodash的深拷贝函数
_.cloneDeep(value) 其中value就是要深拷贝的值
简单例子
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
在Vue中使用
安装
npm i --save lodash
1
在main.js中引入
import _ from 'lodash';
Vue.prototype._ = _;
12
使用
let newObj = this._.cloneDeep(oldObj)
ES6
let块级作用域
- 以一对大括号为一个块
- 不存在变量提升,必须先声明再使用
const
声明常量,即值(内存地址)不能改变的量
-
具有块级作用域
-
必须赋初始值
-
值不能更改,但是可以改数据结构内部的值(内存地址没有变化)
-
const arr = [1,2] arr[0] = "a" arr[1] = "b" //可以 arr = ['a', 'b'] //报错
-
箭头函数
箭头函数没有this,箭头函数在哪定义,this就指向哪
array扩展运算符
...可以将数组或对象转为用逗号分割的参数序列
let arr = [1,2,3]
...arr // 1,2,3
合并数组
方法一:
let arr1 = [1,2,3]
let arr2 = [4,5,6]
let arr3 = [...arr1, ...arr2] //[1,2,3,4,5,6]
方法二:
arr1.push(...arr2)
将伪数组转换为真正数组
[...arr]
array扩展方法
array.from()
array.from( arr ) //将类数组或者可遍历对象转换为真正数组
array.find()
查找第一个符合条件的数组成员
array.find( function() ) //找到返回相应成员,没找到返回undefined
例:
array.find( (item,i) => item.i === 2 )
array.findIndex()
查找第一个符合条件的数组成员的位置
array.findIndex( function() ) //找到返回相应成员下标,没找到返回-1
例:
array.findIndex( (item,i) => item.i === 2 )
array.includes()
判断数组中是否包含指定值,返回布尔值
[1,2,3].includes(2) //true
[1,2,3].includes(4) //false
String扩展方法
startsWith()&endsWith()
startsWith() 判断参数字符串是否在原字符串头部 ,返回布尔值
endsWith() 判断参数字符串是否在原字符串尾部 ,返回布尔值
let str = "hello world !"
str.startsWith('hello') //true
str.endWith('!') //true
repeat()
将原字符串重复n次,返回一个新的字符串
'x'.repeat(3) //'xxx'
'hello'.repeat(2) 'hellohello'
Set数据结构
类似数组,但成员唯一,没有重复值
Set本身是个构造函数
const s = new Set()
Set初始化
const s = new Set([1,2,3,4,5,5,5]) //1,2,3,4,5
数组去重
const s = new Set([1,2,3,4,5,5,5])
let arr = [...s]
实例方法
- add(value): 添加某个值,返回Set结构本身
- delete(value): 删除某个值,返回一个布尔值,表示删除是否成功
- has(value): 返回一个布尔值,表示该值是否为Set的成员
- clear(): 清除所有成员,没有返回值
const s = new set ()
s . add (1) .add(2) .add (3) // 向set结构中添加值
s.delete(2) //删除set结构中的2值
s.has (1) //表示set结构中是否有1这个值返回布尔值
s.clear () //清除set结构中的所有值
遍历
var s = new Set([1,2,3,4,5,6])
s.forEach( value => console.log(value) )