二、JS基础知识(细节还待更新)

105 阅读3分钟

1.值类型与引用类型的区别?

常见值类型
let a  // undefined
let str = 'string'
let num = 100
let bool = true
let s = Symbol('s')

常见引用类型
const obj = { age: 10 }
const arr = [21, 2 ,2 ,21]
const n = null    // 特殊引用类型,指针指向为空地址
function fn() {}  // 特殊引用类型,但不用于存储数据,所以没有拷贝,复制函数这一回事

// 值类型
let a = 100
let b = a
a = 200
console.log(b) // 100  a是a,b是b 并不会改变b的值

// 引用类型
let a = { age: 20 }  // a 存储的其实一个内存地址
let b = a
b.age = 21           // b 和 a这时候指向的是同一个内存地址  
cosnole.log(a.age)   // 21

区别?
    1. 值类型存储到栈内存中
    2. 引用类型存储到堆内存中   

(1)typeof 能判断哪些类型?

let a  // undefined   // typeof a     // undefined
let str = 'string'    // typeof str   // string
let num = 100         // typeof num   // number
let bool = true       // typeof bool  // boolean
let s = Symbol('s')   // typeof s     // symbol
typeof console.log    // function
typeof function(){}   // function   
typeof null           // object
typeof [wq,e]         // object
typeof { a: 100 }     // object

(2)何时使用=== 何时使用 ==

// 除了 == null 之外, 其他都一律用 ===, 例如
     const obj = { x: 100 }
     if (obj.a == null) {}   // 相当于 if (obj.a === null || obj.a === undefined)

2.手写js深拷贝?

const obj = {
    age: 20,
    name: 'John',
    address: {
        city: '成都'
    },
    arr: [2,13,12,312]
}

//1.注意判断值类型和引用类型
//2.注意判断是数组还是对象
//3.递归
/**
 * 手写深拷贝
 * @param { Object } obj 拷贝的对象
 * @author Amorous
 */
 function deepClone(obj = {}) {
    if (typeof obj !== 'object' || obj == null) {
        // obj 是null,或者不是对象和数组,直接返回
        return obj;
    }
    // 初始化返回结果
    let data
    if (obj instanceof Array) {  // instanceof 判断是不是一个数组
        data = []
    } else {
        data = {}
    }
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            // 递归调用!!!
            data[key] = deepClone(obj[key]);
        }
    }
    return data
}

3.变量计算-注意某些类型转换的坑?

1.字符串拼接
    const a = 100 + 10  // 110
    const b = 100 + '10' // '11010'
    const c = true + '10'  // 'true10'
2.==
    100 == '100'   // true
    0 == ''        // true
    0 == false     // true
    false == ''    // true
    null == undefined  // true
    
    // 除了 == null 之外, 其他都一律用 ===, 例如
     const obj = { x: 100 }
     if (obj.a == null) {}   // 相当于 if (obj.a === null || obj.a === undefined)
     
3.if语句和逻辑运算
    truly 变量:  !!a === true 的变量
    falsely变量: !!a === false 的变量
    
 (1)以下是falsely 变量.除此之外都是truly
    !!0 === false
    !!NaN === false
    !!'' === false
    !!null === false
    !!undefined === false
    !!false === false
    
 (2)if 语句  if判断的就是truly变量
    const a = true  或者 const  a = 100
    if (a) {
        // 执行此代码块
    }
    const b = ''  或者  const b = null
    if (b) {
        // 不会执行此代码块
    }
 (3)逻辑运算
    console.log(10 && 0)      // 0          
    console.log(0 && 10)      // 0          
    console.log('' || 'abc')  // abc  
    console.log('abc' || '')  // abc  
    console.log(!window.abc)  // true 

4.变量类型相关面试题

5.js原型的考点和面试题

(1)如何判断一个变量是不是数组?

使用 instanceof 
[] instanceof  Array

(2)手写一个简易的jQuery,考虑插件和扩展性

class Jquery {
    constructor(selector){
        const result = document.querySelectorAll(selector)
        const length = result.length
        for (let i = 0; i < length; i++) {
            this[i] = result[i]
        }
        this.length = length
        this.selector = selector
    }
    get(index) {
        return this[index]
    }
    each() {
        for(let i = 0; i< this.length; i++){
            const elem = this[i]
            fn(elem)
        }
    }
    on(type, fn) {
        return this.each(elem => {
            elem.addEventListener(type, fn, false)
        })
    }
}
 const $p = new Jquery('p')
 $p.get(1)
 $p.each((elem) => console.log(elem.nodeName))
 $p.on('click', () => alert('clicked'))
 
 // 插件
 Jquery.prototype.dialog = function (info) {
    alert(info)
 }
 // 造个轮子
 class myJquery extends Jquery {
    constructor(selector) {
        super(selector)
    }
    // 扩展自己的方法
    addClass(className){}
    style(data){}
 }

(3)class的原型本质,怎么理解?

class是面向对象语法的实现,class它本质上类似于一个模板,通过模板构建一个东西,通过constructor赋值上一些属性及方法
// 这是一个学生类
class Student {
    constructor(name, number)  {
        // this 表示当前构造的实例
        this.name = name
        this.number = number
    }
    // 添加动作
    sayHi() {
        console.log(`姓名${this.name},学号${this.number}`);
    }
    study() {

    }
}

// 通过类声明对象/实例
const zhangsan = new Student('name', 101)
console.log(zhangsan.name);     // name
console.log(zhangsan.number);   // 101
zhangsan.sayHi();               // name 101

6.如何用class实现继承

// 父类
class People {
    constructor(name) {
        this.name = name;
    }
    eat(){
        console.log(`${this.name} eat something`);
    }
}
// 子--学生类
class Student extends People {
    constructor(name, number) {
        super(name)
        this.number = number
    }
    sayHi() {
        console.log(`姓名:${this.name},学号:${this.number}`);
    }
}
// 子--老师类
class Teacher extends People {
    constructor(name, major) {
        super(name)
        this.major = major
    }
    teach() {
        console.log(`姓名:${this.name},课程:${this.major}`);
    }
}
// 实例
const zhangsan = new Student('Amorous', 101)
console.log(zhangsan.name);
console.log(zhangsan.number);
zhangsan.sayHi();
zhangsan.eat();
// 实例
const lisi = new Teacher('Amorous老师', '数学')
console.log(lisi.name);
console.log(lisi.major);
lisi.teach();

7.如何理解js原型(隐式原型和显示原型)?

// class 实际上是函数,可见是语法糖
typeof People // 'function'
typeof Student // 'function'

// 隐式原型(__proto__)和显示原型(prototype)
console.log(zhangsan.__proto__);
console.log(Student.prototype);
console.log(zhangsan.__proto__ === Student.prototype);

1.每个class都有显示原型 prototype
2.每个实例都有隐式原型__proto__
3.实例的__proto__ 指向对应 class的prototype

获取属性zhangsan.name或执行方法zhangsan.sayHi()时
先在自身属性和方法寻找
如果找不到则自动去__proto__中查找

8.instanceof是基于原型链实现的

9.js原型相关面试题

10.什么是作用域?

1.就是`编程语言`在变量中`存储值`,并且能`读取``修改`此值。在变量中`存值``取值`的能力,给程序`赋予了状态`2.如果没有这样的一个概念(规则), 程序虽然可以执行一些任务,但是它们将会受到极大的限制
3.为了解决这一问题就需要制定一些规则,这个规则就是`作用域`

(1)全局作用域

var a = 132;
 b = 123;
 console.log(a, 'a');
 console.log(b, 'b');
 // 没有被var定义的  不管是函数内部还是外部都挂载到window上
 // window 的属性有个特性不加 "." 依然有效
 function test() {
     c = 'wqe'
 }
 test();

(2)函数作用域

function test() {
    var a = 12
    return a + 5
}
console.log(test());  // 17
console.log(a);  // a is not defined  只能在函数内部使用

(3)块级作用域

// 只能在if块中使用
if(true){
    let x = 100
 }
console.log(x);  // x is not defined

(4) 自由变量

1.一个变量在当前作用域没有定义,但被使用了
2.向上级作用域,一层一层依次寻找,直到找到为止
3.如果到全局作用域都没找到,则报错xx is not defined
let a = 0
 function fn1 () {
    let a1 = 100
    function fn2 () {
        let a2 = 200
        function fn3 () {
            let a3 = 300
            return a + a1 + a2 + a3
        }
        fn3()
    }
    fn2
 }
 fn1()

11.什么是闭包?应用场景?

(1)什么是闭包?

1.闭包其实就是作用域应用时候的一个特殊情况
1.函数作为参数被传递
function printf (fn) {
    let a = 100
    fn()
 } 
let a = 200
function fn () {
    console.log(a)
}
printf(fn) // 100
2.函数作为返回值被返回
function create () {
    let a = 100
    return function () {  // 但是函数没有被执行
        console.log(a)
    }
 }
let fn = create()
let a = 200
fn() // 100
// 闭包: 所有的自由变量的查找,是在函数定义的地方,向上级作用域查找
//       不是在执行的地方!!!

(2)闭包应用场景?

1.隐藏数据
2.比如做一个简单地缓存(cache)工具
function createCache() {
    const data = {}  // 闭包中的数据,被隐藏,不被外界访问
    return {
        set: (key, value) => {
            data[key] = val
        },
        get: (key) => {
            return data[key]
        }
    }
}
data.x = 200 // X
// 想要改变data的值只能通过set,get API来设置读取
const x = createCache()
x.set('a', 100)
console.log(x.get('a'));

12.this有几种赋值情况?

(1)this的不同应用场景,如何取值?

(2)手写bind函数?

13.作用域相关的面试题

14.同步和异步有何不同?

区别: 异步是基于JS是单线程语言,异步不会阻塞代码执行,同步会阻塞代码执行
同步?
1.js是单线程语言,只能同时做一件事儿
2.浏览器和node.js 已支持js启动进程,如Web Worker
3.JSDOM渲染共用同一个线程,因为JS可修改DOM结构
4.遇到等待(网络请求,定时任务) 不能卡住
console.log(100)
alert(789)
console.log(200)

异步?
1.异步是基于callback函数形式
console.log(100)
setTimeout(() => {
    console.log(200)
}, 1000)
console.log(300)

15.异步应用场景有哪些?

1.网络请求,如ajax图片加载
//网络请求
console.log('start')
$p.get('./data1.json', (data) => {
    console.log(data);
})
console.log('end');

// 图片加载
function loadImg(src) {
    const p1 = new Promise((resolve, reject) => {
        const img = document.createElement('img')
        img.onload = () => {
            resolve(img)
        }
        img.onerror = () => reject(new Error(`图片加载失败${src}`))
        img.src = src
    })
    return p1
}
const url = 'https://54584c6d0001664502200220-140-140.jpg'
loadImg(url).then(img => {
    console.log(img.width);
    return img
}).then(img => {
    console.log(img.height);
}).catch(err => {
    console.error(err);
})

2.定时任务,如setTimeout(一次),setInterval(循环)
console.log(1) // 1
setTimeout(() => {
    console.log(2)   //5
}, 1000)
console.log(3)   // 2
setTimeout(() => {
    console.log(4)  // 4
}, 0)
console.log(5)  // 3

16.promise的基本使用

// 举个例子 回调地狱 callback hell
$.get(url1, (data1) => {
    console.log(data1);
    // 第二个
    $.get(url1, (data2) => {
        console.log(data2);
        // 第三个
        $.get(url1, (data3) => {
            console.log(data3);
            // .......
        })
    })
})

// Promise 解决
function getData(url) {
    return new Promise((resolve, reject) => {  // 俩个回调函数
        $.ajax({
            url,
            success(data){
                resolve(data) // resolve  返回成功的数据
            },
            error(err){
                reject(err)  // reject 返回失败的数据
            }
        })
    })
}

// 使用
const url1 = '/data1.json'
const url2 = '/data2.json'
const url3 = '/data3.json'
getData(url1).then(data1 => {  使用.then可以一直链式调用下去
    console.log(data1);
    return getData(url2)
}).then(data2 => {
    console.log(data2);
    return getData(url3)
}).then(data3 => {
    console.log(data3);
}).catch(err => console.error(err))