JavaScript基础 | 青训营笔记

64 阅读9分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 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 functionvar c = 1
function c(c) {
  console.log(c)
}
c(2) //c is not a functionvar 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
   不能判断: nullobject  object与array
 instanceof:
   判断对象的具体类型
   可以判断: undefined, nullundefined 代表定义未赋值
null 定义并赋值了, 只是值为 nullvar 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构造函数 指向构造函数本身,用来查看当前对象是由哪个函数创建的

image-20220920164058999.png

image-20220920164935838.png

各种方法

改变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']    //报错
      

image-20220921155919633

箭头函数

箭头函数没有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) )