JS那些事

114 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第12天,点击查看活动详情

内存泄漏

五种常见情况↓

/**
 * 内存泄漏(因为疏忽或错误而造成程序未能释放已经不能在使用的内存)情况
 */

// ① 意外全局变量  --- 使用严格模式
// 'use strict'
// function fn(){
//     // 没有var定义或者使用this定义变量都属于window属性
//     a = 10;
//     this.b = 20
// }
// fn()
// console.log(a,b)

// ② 定时器 --- 不用时候就及时清除定时器 clearInterval(xxx) / 没有及时清除的dom元素 xxx=null

// ③ 闭包
function fun(name){
    function fn(){
        console.log(name)
    }
    return fn
}

var fnn = fun('lily')
fnn()
fnn = null
fnn()   // fnn is not a function


// ⑤ 事件监听 --- 不用时候就取消事件监听

闭包

/**
 * 闭包:函数中返回函数
 * 用途:
 * 1、获取私有作用域的变量,且这些变量能保存在内存中
 * 2、
 */

// 面试题
var funArr = []
for (var i = 0; i < 10; i++) {
    funArr[i] = function () {
        return i
    }
}
console.log(funArr[3]()) // 10 因为for循环跑完再到funArr[3]()执行


/**解决方法:
 * 1、 var 改为 let
 * 2、闭包
 */
var funArr = []
for (var i = 0; i < 10; i++) {
    funArr[i] = (function () {
        let j=i
        return function () {
            return j
        }
    })()
}
console.log(funArr[3]())   // 3

null和undefined

/**
 * null:没有对象,该处不应该有值;转为数值是0;
 * undefined:缺少值;转为数值是NaN(不是数值)
 */

console.log(Number(null))  // 0
console.log(Number(undefined))  // NaN

/**
 * 什么时候有null?
 * 1、 作为函数的参数,表示该函数的参数不是对象;
 * 2、 作为对象原型链的重点
 */

console.log(Object.getPrototypeOf(Object.prototype))  // null

/**
 * 什么时候出现undefined?
 * 1、变量被声明但没有赋值(预解析,变量提升)
 * 2、调用函数时候,应该提供的参数没有提供,该参数等于undefined
 * 3、对象没有赋值的属性,该属性的值为undefined
 * 4、函数没有返回值,默认返回undefined
 */

数组遍历方法

/**
 * 数组方法:
 * 1、foreach : 不改变原数组,没有返回值
 *   arr.forEach(function( item, index, arr){ })
 * 2、map :不改变原数组,有返回值
 *   arr.map(function( item, index, arr){ })
 * 3、filter :过滤,返回一个新数组
 * 4、for...of :返回数组的元素
 * 5、reduce :返回参数函数里的返回值.一般作为累加器计算总价 
 * 6、some():只要检测到有一个元素符合条件就return.
 * 7、every():全部满足返回true,否则false
 * 
 */

var arr = [1, 2, 3, 4, 5, 6, 7, 8]
var res = arr.filter(item => {
    return item > 4
})
console.log(res) // [ 5, 6, 7, 8 ]

for (let value of arr) {
    console.log(value)
}


var total = arr.reduce((firstItem, item) => {
    return firstItem += item
}, 0)
console.log(total) // 36

var res3 = arr.some((item, index) => {
    return item % 2 === 0
})
if (res3) {
    console.log(`以上数有2的倍数`)
} else {
    console.log(`以上数没有2的倍数`)
}

var res4 = arr.every(item => {
    return item > 5
})
console.log(res4)  // false

foreach 和 map 区别

/**
 * foreach 和 map:都是遍历数组,不会修改原数组,区别是:
 * 1、forEach() 方法不会返回执行结果,而是undefined
 * 2、有返回值,返回执行结果
 */

var arr = [1, 2, 3, 4, 5, 6]
var result = arr.forEach((item, index, arr) => {
    // console.log(item)  // 遍历值
    // console.log(index)  // 索引值
    // console.log(arr)  // 数组本身
    return item *2
})
console.log(arr)
console.log(result)  // undefined

var res = arr.map((item,index,arr)=>{
    return item*2
})

console.log(arr)  // [ 1, 2, 3, 4, 5, 6 ]
console.log(res)  //[ 2, 4, 6, 8, 10, 12 ]

call,apply和bind

/**
 * 改变this指向:
 * 如果第一个参数是null和undefined,this默认指向window
 * 1、call: 第二个参数是参数列表
 * 2、apply:第二个参数必须以数组形式传参
 * 3、bind:需要自调用函数,this是永久改变
 */

var name = 'lily'
var obj = {
    name: 'cici',
    age: 25,
    say: function () {
        console.log(this.name)
    },
    sayHobby: function (val1, val2) {
        var str = "I'm " + this.name + ", I like " + val1 + " and " + val2 + ".";
        console.log(str)
    }
}
var obj2 = {
    name: 'grace'
}
obj.say() // cici
// setTimeout(obj.say, 1000) // lily;此时作为settimeout的回调函数调用,this指向全局

// 改变this指向
// setTimeout(obj.say.call(obj), 2000) // cici

// obj.sayHobby('swimming', 'hiking') // cici
obj.sayHobby.call(null, 'swimming', 'hiking')  // I'm lily, I like swimming and hiking.
obj.sayHobby.apply(obj2, ['swimming', 'hiking'])  // I'm grace, I like swimming and hiking.
obj.sayHobby.bind(obj2, 'swimming', 'hiking')()   // I'm grace, I like swimming and hiking.

原型和原型链

image.png

image.png

image.png

/**
 * 使用class实现继承
1、什么是原型?
2、原型链是怎样的?
3、显示原型和隐式原型:
    每一个函数都有一个prototype属性,就是显示原型;
    每个实例对象都有一个__proto__,就是隐式原型;
    ☆ 实例对象的__proto__ === 构造函数的prototype
 */

// 发现学生类,老师类都有一个共同属性name,那么我们定义一个类接收这个共同属性,然后他们来继承
class Person{
    constructor(name){
        this.name = name;
    }
    drink(){
        console.log('人类基本技能:喝水!')
    }
}

// class继承关键字:extends ; super
class Student extends Person{
    constructor(name,score){
        // this.name = name;
        super(name)
        this.score = score
    }
    introduce(){
        console.log(`我是${this.name},考了${this.score}分`)
    }
}

// 实例化
const student = new Student('lily',99)
console.log(student)
student.introduce()


class Teacher extends Person{
    constructor(name,subject){
        // this.name = name;
        super(name)
        this.subject = subject
    }
    teach(){
        console.log(`我是${this.name},教${this.subject}的`)
    }
}

// 创建实例
const teacher = new Teacher('cici','数学')
teacher.teach()
teacher.drink()  

/**
 * 当我们访问一个对象的属性和方法时,首先从自身出发寻找,如果找不到就从它的原型上找,还是没找到,继续往原型的原型上找,这个路径就成为原型链
 * 检验这个属性或方法是否在对象本身上:hasownproperty
 */

console.log(teacher.hasOwnProperty('name'))  // true
console.log(teacher.hasOwnProperty('teach'))  // false

image.png