javaScript面试必备基础知识
值类型和引用类型的区别
值类型存值,引用类型存地址
常见值类型
let a //undefined
const s='abc'
const n=100
const b=true
const s=Symbol('s')
常见引用类型
const obj={ }
const arr=[ ]
const n=null //特殊引用类型,指针指向为空地址
//函数 特殊引用类型,不用于存储数据,所有没有“拷贝,复制函数”这一说
//值类型
let a = 100;
let b = a;
a = 200;
console.log(b);//100
//引用类型
let a = { age: 10 };
let b = a;
a.age = 20;
console.log(b.age)//20
const obj1={x:100,y:200}
const obj2=obj1
let x1=obj1.x
obj2.x=101
x1=102
console.log(obj1) //{ x: 101, y: 200 }
typeof能判断哪些类型
识别所有值类型
识别函数
判断是否是引用类型Object(不可再细分)
//判断所有值类型
console.log(typeof a);//undefined
console.log(typeof (str = 'abc'));//string
console.log(typeof (n = 100));//number
console.log(typeof (b = true));//boolean
s = Symbol('S')
console.log(typeof s);//symbol
//判断函数
console.log(typeof console.log) //function
console.log(typeof function () { })//function
//能识别引用类型
console.log(typeof null);//object
console.log(typeof ['a', 'b']);//object
console.log(typeof { x: 20 });//object
手写深拷贝
/* 深拷贝*/
const obj1 = {
age: 20,
name: 'xxx',
address: {
city: 'beijing',
a: {
c: {
d: 10,
}
}
},
arr: ['a', 'b', 'c']
}
const obj2 = deepClone(obj1)
console.log(obj2);
obj2.address.city = 'shanghai'
obj2.arr[0] = 'a1'
console.log(obj1.address.city) //'beijing'
console.log(obj1.arr[0])//'a'
/**
* 深拷贝
* @param {Object} obj 要拷贝的对象
*/
function deepClone(obj = {}) {
if (typeof obj !== 'object' || obj == null) {
// obj 是 null ,或者不是对象和数组,直接返回
return obj
}
// 初始化返回结果
let result
if (obj instanceof Array) {
result = []
} else {
result = {}
}
for (let key in obj) {
// 保证 key 不是原型的属性
if (obj.hasOwnProperty(key)) {
// 递归调用!!!
result[key] = deepClone(obj[key])
}
}
// 返回结果
return result
}
变量计算 —类型转换
字符串拼接
const a=100+10 //110
const b=100+'10' //'10010'
const c=true+'10' //'true10'
何时使用===
何时使用==
==运算符
除了==null之外,其他一律用===
100=='100' //true
0==' '//true
0==false //true
false==' '//true
null==undefined //true
const obj={x:10}
if(obj.a==null){}
//相当于 if(obj.a===null||obj.a===undefined){}
if语句和逻辑运算
truly变量: !!a===true
falsely变量:!!a===false
除以下是falsely变量,其余都是truely变量
0,NaN,‘ ’,null,undefined,false
if语句的逻辑判断根据truely变量和falsely变量走分支
class和继承
继承extends ;类型判断instanceof
// 父类
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 xialuo = new Student('夏洛', 100)
console.log(xialuo.name)
console.log(xialuo.number)
xialuo.sayHi()
xialuo.eat()
// 实例
const wanglaoshi = new Teacher('王老师', '语文')
console.log(wanglaoshi.name)
console.log(wanglaoshi.major)
wanglaoshi.teach()
wanglaoshi.eat()
//类型判断-instanceof
console.log("-----instanceof-----");
console.log(xialuo instanceof Student);
console.log(xialuo instanceof People);
console.log(xialuo instanceof Object);
console.log([] instanceof Array);
原型和原型链
原型关系
每个class都有显式原型prototype
每个实例都有隐式原型__proto__
实例的__proto__指向对应class的prototype
基于原型的执行规则
获取属性xialuo.name或执行方法xialuo.sayHi( )时,先在自身属性和方法查找
如果找不到,则自动去__proto__中查找
注意
class是ES6语法规范,由ECMA委员会发布
ECMA只规定语法规则,即我们代码的书写规范,不规定如何实现
//class实际上是函数
console.log(typeof People);//function
console.log(typeof Student);//function
//隐式原型和显式原型
console.log(xialuo.__proto__) //Student {}
console.log(Student.prototype) //Student {}
console.log(xialuo.__proto__===Student.prototype) //true
console.log(Student.prototype.__proto__); //People{}
console.log(People.prototype);//People{}
console.log(People.prototype === Student.prototype.__proto__);//true
class的原型本质,怎么理解?
如何准确判断一个变量是不是数组?
a instanceof Array
手写一个简易的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(fn) {
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)
})
}
// 扩展很多 DOM API
}
// 插件
jQuery.prototype.dialog = function (info) {
alert(info)
}
// “造轮子”
class myJQuery extends jQuery {
constructor(selector) {
super(selector)
}
// 扩展自己的方法
addClass(className) {
}
style(data) {
}
}
// const $p = new jQuery('p')
// $p.get(1)
// $p.each((elem) => console.log(elem.nodeName))
// $p.on('click', () => alert('clicked'))
作用域和自由变量
作用域:变量的合法使用范围
全局作用域
函数作用域
块级作用域(ES6新增)
自由变量
一个变量在当前作用域没有被定义,但被使用了
向上级作用域,一层一层依次寻找,直至找到为止
如果全局作用域都没找到,则报错xx is not defined
![](https://img-blog.csdnimg.cn/20210714092911489.png)
如图:fn3()内没有定义a,a1,a2却被使用,那么一级一级依次向上查找。
闭包
作用域应用的特殊情况,有两种表现:
函数作为参数被传递
函数作为返回值被返回
所有的自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方!!!
函数作为返回值
function create() {
const a = 100
return function () {
console.log(a)
}
}
const fn = create()
const a = 200
fn() // 100
函数作为参数被传递
function print(fn) {
const a = 200
fn()
}
const a = 100
function fn() {
console.log(a)
}
print(fn) // 100
this
this的不同应用场景,如何取值?
作为普通函数:返回window
使用call apply bind:传入什么返回什么。call()、apply()、bind() 都是用来重定义 this 这个对象的!
作为对象方法被调用:返回对象本身
在class方法中调用:当前实例本身
箭头函数
箭头函数中this的取值取上级作用域的this
this取什么值,是在函数执行时确定的,不是函数定义时确定的
左边:这个函数的执行是setTimeout本身触发的执行,作为普通函数而不是对象方法被执行。
右边:箭头函数中this的永远取它上级作用域的this,自己本身不会决定this的值。
class方法中调用:
![](https://img-blog.csdnimg.cn/20210714100609558.png)
手写bind函数
第一个参数传入要绑定的this对象。
// 模拟 bind
Function.prototype.bind1 = function () {
// 将参数拆解为数组
const args = Array.prototype.slice.call(arguments)
// 获取 this(数组第一项)
const t = args.shift()
// fn1.bind(...) 中的 fn1
const self = this
// 返回一个函数
return function () {
return self.apply(t, args)
}
}
function fn1(a, b, c) {
console.log('this', this)
console.log(a, b, c)
return 'this is fn1'
}
const fn2 = fn1.bind1({ x: 100 }, 10, 20, 30)
const res = fn2()
console.log(res)
实际开发中闭包的应用场景,举例说明
闭包应用场景
隐藏数据
// 闭包隐藏数据,只提供 API
function createCache() {
const data = {} // 闭包中的数据,被隐藏,不被外界访问
return {
set: function (key, val) {
data[key] = val
},
get: function (key) {
return data[key]
}
}
}
const c = createCache()
c.set('a', 100)
console.log( c.get('a') )
创建10个a标签,点击的时候弹出来对应的序号
//创建10个a标签,点击的时候弹出来对应的序号
//不论点哪个,弹出都是10
let i, a
for (i = 0; i < 10; i++) {
a = document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click', function (e) {
e.preventDefault()
alert(i)
})
document.body.appendChild(a)
}
//改进 弹出0 1 2
let a
for (let i = 0; i < 10; i++) {
a = document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click', function (e) {
e.preventDefault()
alert(i)
})
document.body.appendChild(a)
}
全局作用域vs块级作用域
块级作用域:每次循环产生一个新的块级作用域。click不是立即执行的函数。
前者,i是全局变量,在click之前i就变成10了。
后者,i是块级作用域,每次循环产生新作用域,click之前变成对应 i 值。