JS 基础知识巩固(JS面试宝典-全面透彻)

第一章:JS 基础上篇(js 类型)

  1. 值类型
var a = 100
var b = a
a = 200
console.log(b)  // 100
  1. 引用类型
var a = {age:100}
var b = a 
a.age = 200
console.log(a.age) // 200

问题:为什么对值类型和引用类型进行修改会出现不同的结果呢?
答:值类型:存的是具体的数值,比如 a=200,那么 a 变量内存中地址存的就是 200。所以改变 a=100 ,仅仅只是影响到了 a。
引用类型:内存中存的是对象的地址,所以 a 和 b 此刻指向了同一个对象的地址。改变 a.age 就会同时影响到 b.age。

  1. 引用类型包括
    对象、数组、函数(为什么需要用到引用呢?因为这些类型可能非常大,每次赋值都新建一个新对象,就会比较浪费空间)
  2. typeof 运算符
typeof undefined        // undefined
typeof 'abc'            // string
typeof 123              // number
typeof true             // boolean
typeof {}               // object
typeof []               // object
typeof null             // object (它也是引用类型)
typeof console.log      // function

结论: typeof 能区分值类型;对于引用类型只能区分 function 其他无法区分。

  1. 问:JS 中有哪些内置函数 -- 数据封装类对象
    答:
Object
Array
Boolean 
Number
String
Function
Date
RegExp
Error

第二章:JS 基础上篇(原型和原型链)

  1. var a = {} 其实是 var a = new Object() 的语法糖
  2. var a = [] 其实是 var a = new Array() 的语法糖
  3. function Foo(){...} 其实是 var Foo = new Function(){...}
  4. 使用 instanceof 判断一个函数是否是一个变量的构造函数.

总结:引用类型都有一个构造函数

  1. 下写一个原型链继承的例子
function Animal() {
    this.eat = function () {
        console.log('animal eat')
    }
}

function Dog() {
    this.bark = function () {
        console.log('dog bark')
    }
}

Dog.prototype = new Animal();

let hashiqi = new Dog()

hashiqi.bark()
hashiqi.eat()
  1. 描述 new 一个对象的过程
  • 创建一个新对象
  • this 指向这个新对象
  • 执行代码,即对 this 赋值
  • 返回 this

第三章:JS 基础中篇(作用域和闭包)

  1. 表达式和声明的区别
fn()                        // 输出: fn
// fn1()                    // 输出: fn1 is not a function
console.log(a);             // 输出: undefined

// 这个叫做"声明"
function fn() {
    console.log('fn');
}

// 这个叫做"表达式"
var fn1 = function () {
    console.log('fn1');
}

// 这个叫做"表达式"
var a = 100;
  • JavaScript 解释器中存在一种变量声明被提升的机制,也就是说函数声明会被提升到作用域的最前面,即使写代码的时候是写在最后面,也还是会被提升至最前面。
  • 而函数表达式创建的函数是在运行时进行赋值,且要等到表达式赋值完成后才能调用。

前面的函数,相当于如下写法:

// 这个叫做"函数声明"
function fn() {
    console.log('fn');
}
var a = undefined;
var fn1 = undefined;

fn()                        // 输出: fn
// fn1()        // error: fn1 is not a function
console.log(fn1);   // 输出: undefined
console.log(a); // 输出: undefined


// 这个叫做"表达式"
var fn1 = function () {
    console.log('fn1');
}

// 这个叫做"表达式"
var a = 100;

2.函数内部会默认有 this 和 arguments 自带变量

fn('zhansan')
function fn(name) {
    console.log(this);                //输出:复杂对象
    console.log(arguments);   //输出:{ '0': 'zhansan' }

    age = 20
    console.log(name, age);   //输出:zhansan 20
    var age 
}
  1. this 要在执行时才能确认值,定义时无法确认
let a = {
    name: 'A',
    fn: function () {
        console.log(this.name)
        // console.log(this)
    }
}

a.fn()                // this === a
a.fn.call({ name: 'B' })  // this === {name:'B'}
this.name = 'here'  
this.fn1 = a.fn
this.fn1()         // this === window
  1. JavaScript 无块级作用域
if (true) {
    var name = 'zhansan'
}
console.log(name);   // 输出:  name === 'zhansan'
  1. JavaScript 有 函数作用域和全局作用域
var a = 'global'
function fn() {
    var a = 'function'
    console.log(a);
}
fn()    // 输出 'fn'
console.log(a);    // 输出 'global'

6.作用域链:就是函数定义的时候,内部使用的变量没有进行定义,会向父级作用域寻找该变量。

函数作用域中使用没有在本函数作用域中的变量叫做自由变量:自由变量的定义一定是在定义时候的父级作用域去寻找。

var a = 100
function F1(){
    var b = 200
    function F2(){
        var c = 300
        console.log(a);
        console.log(b);
        console.log(c);
    }
    F2()
}
F1()
  1. 闭包-闭包的定义,自己从下面 代码一作为返回值、代码二作为参数 中体会。
// 代码一:闭包作为返回值
function F1() {
    var a = 100
    // 返回一个函数(函数作为返回值)
    return function(){
        console.log(a);  // 自由变量,父级作用域寻找 
    }
}
// f1 得到一个函数
var f1 = F1()
var a = 200
f1() // 输出: 100
// 代码二:闭包作为参数值
function F1() {
    var a = 100
    // 返回一个函数(函数作为返回值)
    return function(){
        console.log(a); // 自由变量,父级作用域寻找
    }
}
// f1 得到一个函数
var f1 = F1()
function F2(fn) { 
    var a = 200
    fn()
}
F2(f1)  // 依然输出:    100

闭包使用的时候经常会搞不清楚自由变量是什么?请返回阅读上一条自由变量相关内容。

  1. 闭包实际应用中主要用于封装变量,收敛权限。
function isFirstLoad() {
    var _list = []
    return function (id) {
        if (_list.indexOf(id) >= 0) {
            return false
        } else {
            _list.push(id)
            return true
        }
    }
}

// 使用
var firstLoad = isFirstLoad()
console.log(firstLoad(10));     // true
console.log(firstLoad(10));     // false
console.log(firstLoad(20));     // true

上面的代码,使用闭包,就会将 _list 很好的隐藏起来,保护它的其他操作权限。

第四章:JS 基础下篇(异步和单线程、其他知识点)

  1. 什么是异步?什么是同步?setTimeout() 就是异步(不阻塞代码),alert()就是同步(阻塞代码)。
  2. 什么是单线程?就是一次只能做一件事情,类似一次执行一行代码。
  3. 日期
Date.now() // 获取当前时间毫秒数
var dt = new Date()
dt.getTime()  // 获取毫秒数
dt.getFullYear()  // 年
dt.getMonth()  // 月(0-1)注意:这里是比较特殊
dt.getDate()  // 日 (1-31)
dt.gatHours() // 小时(0-23)
dt.getMinutes() // 分钟 (0-59)
dt.getSeconds()  // 秒 (0-59)
  1. math 主要应用:math.random() 获取一个 0-1 之间的随机数

第五章:JS-Web-API 上篇(DOM 、 BOM)

  1. 常说的 JS(浏览器执行的 JS)包含两部分:

① JS 基础知识( ECMA262 标准 )
② JS-Web-API( W3C 标准 )

  1. DOM 的本质是什么?

浏览器把拿到的 HTML 代码,结构化成一个浏览器能识别并且 JS 可操作的一个模型而已。

  1. 获取 DOM 节点(最常用的几种)
var div1 = document.getElementById('div1')  //元素
var divList = document.getElementsByTagName('div')  //集合
var containerList = document.getElementsByClassName('container')    //集合
console.log(divList.length)
console.log(divList[0])
console.log(containerList.length)

var pList = document.querySelectorAll('p')  //集合
var p = pList[0]
console.log(p.style.width)
p.style.width = '200px' // 改变宽度
console.log(p.style.width)
console.log(p.className)
p.className = 'p2class'
// 获取 nodeName 和 nodeType
console.log(p.nodeName)
console.log(p.nodeType)
  1. Property 和 Attribute 中文翻译都是‘属性’,有什么不同呢?
var pList = document.querySelectorAll('p')  //集合
var p = pList[0]
p.style.width   // 这里就是 property  改的是 JS 对象的属性
p.setAttribute('data-name', 'helloworld')  //  改的是 dom 文档的属性
p.setAttribute('style', 'font-size: 26px;')
console.log(p.getAttribute('data-name'))
console.log(p.getAttribute('style'))
  1. navigator & screen (BOM 内置变量)
// navigator 判断浏览器
var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
console.log(!!isChrome)

// screen 判断窗体大小
console.log(screen.width)
console.log(screen.height)
  1. location & history
// location
console.log(location.href) // 'https://www.baidu.com/s?wd=aaa'
console.log(location.protocol) // 'https:'
console.log(location.host) // www.baidu.com
console.log(location.pathname) // '/s'
console.log(location.search) // ?号后面的内容  wd=aaa

// history
history.back()
history.forward()

第六章:JS-Web-API 下篇(事件、跨域、存储)

  1. 通用事件绑定(写一个通用事件绑定方法,可以减少方法名长度)

event.preventDefault() // 阻止控件的默认事件

var btn = document.getElementById('btn1')
btn.addEventListener('click',(event)=>{
    console.log('click')
})

function bindEvent(element, type, fn) {
    element.addEventListener(type, fn)
}
var a = document.getElementById('link1')
bindEvent(a, 'click', (event)=> {
    event.preventDefault() // 阻止 a 标签默认行为
    alert('clicked')
})
  1. 事件冒泡

event.stopPropagation() // 阻止事件冒泡

问:如何让所有<p>绑定点击事件,使得点击激活;执行激活操作,点击取消,执行取消操作。

<body>
    <div id="div1">
        <p id="p1">激活</p>
        <p id="p2">取消</p>
        <p id="p3">取消</p>
        <p id="p4">取消</p>
    </div>
    <div id="div1">
        <p id="p5">取消</p>
        <p id="p6">取消</p>
        <p id="p7">取消</p>
        <p id="p8">取消</p>
    </div>
</body>

答:根据事件冒泡原理,我们可以先绑定 body 的事件。

<script>
    var body = document.body
    body.addEventListener('click',(event)=>{
        alert('取消')
    })
    var activeDiv = document.getElementById('p1')
    activeDiv.addEventListener('click',(event)=>{
        event.stopPropagation();    //阻止事件继续向上冒泡
        alert('激活');
    })
</script>
  1. 问题如下:如何让程序实现点击 a 标签,弹出 a 标签内的内容?
<div id="div1">
    <a href="#">a1</a>
    <a href="#">a2</a>
    <a href="#">a3</a>
    <a href="#">a4</a>
    <!-- 会随时新增更多 a 标签 -->
</div>

答:通过 event.target 可以获取点击事件触发的最顶层控件

<script>
    let div1 = document.getElementById('div1')
    div1.addEventListener('click',(event)=>{
        let target = event.target
        if (target.nodeName === 'A') {
            alert(target.innerHTML)
        }
    })
</script>
  1. 什么是跨域?
  • 浏览器有同源策略,不允许 ajax 访问其他域接口。
  • 跨域条件:协议、域名、端口,有一个不同就算跨域。
  1. 有哪三个标签运行跨域访问?
  • <img src=xxx>
  • <link src=xxx>
  • <script src=xxx>
  1. 三个标签的特殊应用场景?
  • <img> 用于打点统计,统计网站可能是其他域
  • <link><script> 可以使用 CDN,CDN 也是其他域
  • <script> 可以用 JSONP。👇下面代码展示什么是 JSONP
<script>
  window.callback = function (data) {
    // 这是我们跨域得到的信息
    console.log(data)
  }
</script>
<script src="http://xxx.com/api.js"></script>
<!--以上将返回 callback({x:100, y:200}) -->
  1. 本地存储之 cookie
  • 本身用于客户端和服务器端通信
  • 但是它有本地存储的功能,于是就被“借用”
  • 使用 document.cookie = ... 获取和修改即可
  1. cookie 用于存储的缺点
  • 存储量太小,只有 4KB
  • 所有 http 请求都带着,会影响获取资源的效率
  • API 简单,需要封装才能用 document.cookie = ...
  1. locationStorage 和 sessionStorage
  • HTML5 专门为存储而设计,最大容量 5M
  • API 简单易用
  • localStorage.setItem(key, value); localStorage.getItem(key);
  1. locationStorage 和 sessionStorage
  • iOS safari 隐藏模式下
  • localStorage.getItem 会报错
  • 建议统一使用 try-catch 封装

第七章:模块化

  1. 先看不用模块化的代码会出现的问题。有如下说明代码:
// util.js
function getFormatDate(date, type){
  // type === 1 返回 2019-04-30
  // type === 2 返回 2019 年4 月 15 日
  // ...
}

// a-util.js
function aGetFormatDate(date) {
  // 要求返回 返回 2019 年4 月 15 日 格式
  return getFormatDate(date, 2)
}

// a.js
let dt = new Date()
console.log(aGetFormatDate(dt))

对于上述代码的调用:

<script src="util.js"></script>
<script src="a-util.js"></script>
<script src="a.js"></script>
  1. 这些代码中的函数必须是全局变量,才能暴露给使用方。容易造成全局变量污染。
  2. a.js 知道药引用 a-util.js ,但是他知道还需要依赖于 util.js 吗?
  1. 使用模块化。有如下说明代码:
// util.js
export {
  getFormatDate: function (date, type){
    // type === 1 返回 2019-04-30
    // type === 2 返回 2019 年4 月 15 日
    // ...
  }
}

// a-util.js
var getFormatDate = require('util.js')
export {
  aGetFormatDate: function (date){
    return getFormatDate(date, 2)
  }
}

// a.js
var aGetFormatDate = require('a-util.js')
let dt = new Date()
console.log(aGetFormatDate(dt))

模块化的代码有如下好处:

  • 直接<script src="a.js"></script>,其他的根据依赖关系自动引用。
  • 那两个函数,没必要做成全局变量,不会来带污染和覆盖。

第八章:运行环境

  1. 浏览器加载一个资源的过程
  1. 浏览器根据 DNS 服务器得到域名的 IP 地址
  2. 向这个 IP 的机器发送 http 请求
  3. 服务器收到、处理并返回 http 请求
  4. 浏览器得到返回内容
  1. 浏览器渲染页面的过程
  1. 根据 HTML 结构生成 DOM Tree
  2. 根据 CSS 生成 CSSOM
  3. 将 DOM 和 CSSOM 整合形成 RenderTree
  4. 根据 RenderTree 开始渲染和展示
  5. 遇到 <script> 时,会执行并阻塞渲染
  1. window.onload 和 DOMContentLoaded
window.addEventListener('load', function(){
  // 页面全部资源加载完成才会执行,包括图片、视频等
})
document.addEventListener('DOMContentLoaded', function() {
  // DOM 渲染完成即可执行,此时图片、视频还可能没有加载完
})
  1. 性能优化

原则

  • 多使用内存、缓存
  • 减少 CPU 计算、减少网络请求 、 减少 DOM 操作
  1. 加载资源优化
  • 静态资源的压缩合并
  • 静态资源缓存
  • 使用 CDN 让资源加载更快
  • 使用 SSR 后端渲染,数据直接输出到 HTML 中
  1. 渲染优化
  • CSS 放前面,JS 放后面
  • 懒加载(图片懒加载、下拉加载更多)
  • 减少 DOM 查询,对 DOM 查询做缓存
  • 减少 DOM 操作,多个操作尽量合并在一起执行
  • 事件节流
  • 尽早执行操作(如 DOMContentLoaded)
  1. 安全性之 XSS (全称跨站脚本攻击)
  • 在新浪博客写一篇文章,同时偷偷插入一段<script>
  • 攻击代码中,获取 cookie ,发送到自己的服务器
  • 发布博客,有人查看博客内容,会执行代码。
  • 解决方案:前端或者后端替换关键字,例如替换 < 为 &lt ; > 为 &gt ;
  1. 安全性之 XSRF (全称跨站请求伪造)
  • 你已登录一个购物网站,正在浏览商品
  • 该网站的付费接口是 xxx.com/pay?id=100 但是没有任何验证
  • 然后你收到一封邮件,隐藏着<img src=xxx.com/pay?id=100>
  • 当你查看邮件的时候,就已经悄悄的付费购买了
  • 解决方案:增加验证流程,如输入指纹、密码、短信验证码。或者服务端更多信息的校验。

推荐阅读更多精彩内容

  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 8,360评论 0 31
  • 1.几种基本数据类型?复杂数据类型?值类型和引用数据类型?堆栈数据结构? 基本数据类型:Undefined、Nul...
    极乐君阅读 3,389评论 1 99
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 3,270评论 0 12
  • 0. 写在前面 当你开始工作时,你不是在给你自己写代码,而是为后来人写代码。 —— Nichloas C. Zak...
    康斌阅读 3,073评论 0 39
  • 第3章 基本概念 3.1 语法 3.2 关键字和保留字 3.3 变量 3.4 数据类型 5种简单数据类型:Unde...
    RickCole阅读 1,144评论 0 11