JavaSript面试课-2020-05-22

164 阅读6分钟

一、JS基础-变量类型和计算

1 JS 值类型和引用类型的区别

const n = null //特殊的引用类型,指针指向空地址
function fn(){}  //特殊的引用类型,不用于存储数据,没有'拷贝、复制函数'一说

1、type of运算符

1.1识别所有的值类型 1.2识别函数

    typeof console.log // 'function'

2、深拷贝

      function deepClone(obj = {}){
          if(typeof obj !=='object' || obj == null) return obj;
          //初始化返回结果
          let res 
          if(obj instanceof Array){
            res = []
            }else{
               res = {}
            }
        for(let key in obj){
         //确保 key 不是原型的属性
         if(obj.hasOwnProperty(key)){
         res[key] = deepClone(obj[key])
         }
        }
        return res
      }  

3、变量计算-数据类型转换

(1)字符串拼接

(2)==运算符

    //除了 == null,其余一律使用 === 
    const obj = { x:100}
    if(obj.a == null){}
    //等价于
    if(obj.a === null || obj.a === undefind){}

(3)if语句和逻辑运算

truily变量 !!a === true 的变量
falsely变量 !!a === false 的变量
例如: 0 NaN '' null indefined false

二、原型和原型链(超重要,必问!!!!)

1、如何准确判断一个变量是不是数组?

a instanceof Array

2、手写一个简易的jqurey:不会!!!

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
	}
	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)
		})
	}
}

        //插件扩展
        jQuery.prototype.dialog = function(info){
                alert(info)
        }
        //造轮子
        class myJquery extends jQuery{
                constructor(selector) {
                    super(selector)
                        //新增轮子逻辑

                }
        }
        //使用
        const $p = new jQuery('p')
        $p.get(2)
        $p.each(ele=>console.log(ele.nodeName))
        $p.on('click',()=>alert('clicked'))

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

知识点:class的继承、类型判断instanceof、原型和原型链

    class Student {
        constructor(name, age){
        this.name = name
        this.age = age
        }
        sayHi(){
            console.log(
            `姓名${this.name}, 年龄${this.age}`
            )
        }
    }
    
    //通过 new 对象实例
    const tao = new Student('阿涛'120)

2、继承

//父类
    class People{
        constructor(name){
        this.name = name
        this.age = age
        }
        eat(){
            console.log(
            `姓名${this.name}, eat something`
            )
        }
    }
//子类
    class Student extends People{
       constructor(name,age){
       super(name)
        this.age = age
        }
      sayHi(){
      console.log( `姓名${this.name}, 年龄${this.age}` ) 
      }
    }
    //子类
    class Teacher extends People{
       constructor(name,major){
       super(name)
        this.major = major
        }
      teach(){
      console.log( `姓名${this.name}, 专业${this.major}` ) 
      }
    }
    //通过 new 对象实例
    const tao = new Student('阿涛'120)
    const li = new Teacher('李老师','语文')
  • extends关键字来继承
  • super执行父类的构造函数

3、JS原型

  • 隐式原型:__proto__
  • 显示原型:prototype

子类.proto===父类.prototype //true

原型关系:

1、每个class(构造函数)都有显示原型prototype,

2、每个实例都要隐式原型__proto__,

3、实例的隐式原型__proto__指向对应class的显示原型prototype

4、原型链

三、作用域和闭包(很重要!!!)

题目:创建10个 标签点击哪个,弹出哪个的序号

                        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)
			}

1、作用域与自由变量

2.1作用域: 全局作用域:window对象、document对象。。。

函数作用域:只能在当前函数里使用

块级作用域:在循环,条件等语句中使用

*2.2自由变量 *

在当前作用域没有被定义,但被使用了,

向上级作用域一层一层依次寻找,直到找到为止

如果全局作用域都没找到,直接报错 xxx is not defined

2、闭包: 所有自由变量的查找,是在函数定义的地方,向上级作用域查找,而不是执行的地方!!! 作用域应用的两种情况:

  • (1)函数作为参数被传递
function print(fn) {
	let a = 200
	fn()
}
let a = 100

function fn() {
	console.log(a)
}
print(fn) //100
  • (2)函数作为返回值被返回
function create() {
	let a = 100
	return function() {
		console.log(a)
	}
}
let fn = create()
let a = 200
fn() //100

实际开发中应用:

隐藏数据,只提供API

			//缓存工具函数
			function createCache() {
				//闭包中的数据,被隐藏,不必外界访问
				const data = {}
				return {
					set: function(key, val) {
						return data[key] = val
					},
					get: function(key) {
						return data[key]
					}
				}
			}

			const c = createCache()
			c.set('a', 100)
			console.log(c.get('a'))

3、this:取什么值,是在函数执行中确定的,不是在函数定义确定的!!!

image.png

  • 作为普通函数
  • 使用call、apply 、bind]()
  • 作为对象方法调用
  • 定时器延时器调用

image.png

  • 在class方法中调用:this指的是new出来的实例对象

image.png

  • 箭头函数:上级作用域的值 4-4手写bind函数:
			//模拟bind
			Function.prototype.bind1 = function() {
				//将参数拆解为数组
				const args = Array.prototype.slice.call(arguments)
				//获取this(数组第一项)
				const t = args.shift()
				const self = this
				//返回一个函数
				return function() {
					return self.apply(t, args)
				}

			}
    

四、异步和单线程

同步和异步 的区别

手写promise加载一张图片 前端使用异步的场景有哪些?

1、单线程和异步

JS单线程语言,只能同时做一件事

浏览器和node.js已经支持JS启动进程,如Web Worker

JS和DOM渲染共用同一个线程,因为JS可以修改DOM结构

遇到等待(网络请求,定时任务)不能卡住

需要异步

回调callback函数形式

  • 同步和异步是基于JS单线程语言的,异步不会阻塞代码执行,同步会阻塞代码执行

2、异步的应用场景

网络请求,如ajax图片加载

定时任务,如setTimeout

3、callback 和Promise

5-3 5-6看完es6再看

二、JS WebAPI

一、DOM

题目:

DOM属于哪种数据结构?DOM树

DOM操作的常用API:节点操作和结构操作

attr和property的区别:

  • property修改的是对象属性,不会体现在html结构中;attribute修改的是html属性,会改变html结构;这两个都会引起DOM重新渲染,尽量用property

一次性插入多个DOM节点,考虑性能:先创建一个文档片段,将创建的元素一次性插入到文档片段中,最后再将文档片段插入到DOM树

知识点:

1、DOM的本质

从HTML文件解析出来的DOM树

2、DOM节点操作

1、获取DOM节点

2、attribute:设置的是标签的属性]() image.png 3、property:修改JS变量的属性]() image.png

3、DOM结构操作

1、新增/插入节点

image.png

2、获取子元素列表,获取父元素

image.png

3、删除子节点

image.png

4、DOM性能

  • Dom操作昂贵,避免频繁查询
  • 对dom查询做缓存

image.png

  • 将频繁改为一次性
             const listNode = document.getElementById('list')
			//创建一个文档片段
			const frag = document.createDocumentFragment()
			for (let x = 0; x < 10; x++) {
				const li = document.createElement("li")
				li.innerHTML = 'List item' + x
				frag.appendChild(li)
			}
			listNode.appendChild(frag)

二、BOM

题目:

如何识别浏览器类型?

分析拆解URL各个部分

知识点:

navigator:查看当前浏览器信息 screen:查看屏幕的宽高

image.png

location history

image.png

三、事件绑定和冒泡

编写一个事件监听函数

			function bindEvent(elem, type, selector, fn) {
				if (fn == null) {
					fn = selector
					selector = null
				}
				elem.addEventListener(type, event => {
					const target = event.target
					if (selector) {
                                        //主要是用来判断当前DOW节点是否能完全匹配对应的CSS[选择器],如果匹配成功,返回true,反之则返回false
						if (target.matches(selector)) {
							fn.call(target, event)
						}
					} else {
						//普通绑定
						fn.call(target, event)
					}
				})
			}

2d456ed07cfbc45c9648d9ac848707b.png

描述事件冒泡流程:

基于DOM树结构,事件会顺着触发元素往上冒泡,应用场景是事件委托

image.png event.preventDefault()

无限下拉图片列表,如何监听每个图片的点击?

事件代理,用e.target获取触发元素,用matches来判断是否是触发元素

  • 代码简洁、减少浏览器内存占用,在父元素上绑定,不要滥用

四、ajax

针对于浏览器-同源策略——(比如爬虫,可以拿到第三方的数据)

无视跨域的三种

  • <img src= 跨域的地址 /> 统计打点,可使用第三方统计服务 pv uv
  • <link src= 跨域的地址 /> 可使用 CDN 一般是 外域
  • <scriot src= 跨域的地址 /> 可实现跨域

跨域:(必须serve 端同意)

image.png

image.png

image.png 手写ajax

function ajax(url) {
				const p = new Promise((resolve, reject) => {
					const xhr =new XMLHttpRequest()
					xhr.open('GET', './data/teest.json', true)
					xhr.onreadystatechange = function() {
						if (xhr.readyState === 4) {
							if (xhr.status === 200) {
								resolve(JSON.parse(xhr.responseText))
							} else if (xhr.status === 404) {
								reject(new Error('404 no found'))
							}
						}
					}
                                        xhr.send(null)
				})
				return p
			}

五、存储

cookie

  • 本身用于浏览器和 serve 通讯
  • 被借用 到 本地存储
  • 可用 document.cookie = "xxx"来修改,挨个累加追加

image.png

image.png

image.png

			function debounce(fn, delay = 500) {
				let timer = null
				return function() {
					if (timer) {
						clearTimeout(timer)
					}
					timer = setTimeout(() => {
						fn.apply(this, arguments)
					}, delay)
				}
			}
			input.addEventListener('keyup', debounce(function() {
				console.log(input.value)
			}, 1000))

image.png

			function throttle(fn, delay = 100) {
				let timer = null
				return function() {
					if (timer) {
						return
					}
					timer = setTimeout(() => {
						fn.apply(this, arguments)
						timer = null
					}, delay)
				}
			}
			const div = document.getElementById('div')
			div.addEventListener('drag', throttle(function(e) {
				console.log(e.offsetX, e.offsetY)
			}, 600))

logo_64.png