JS知识点梳理

前言

快要开始面试了,在这里记录各种前端知识点,方便自己复习。每一个问题下不止记录该问题的答案,还会记录所有相关的知识点。欢迎大家一起交流学习。

变量类型和计算

1.typeof能判断哪些类型?

值类型:undifined、string、number、布尔、symbol(ES6)
引用类型:对象、数组、null(特殊,指针指向空地址)、函数
typeof可以识别所有的值类型,识别函数,但是只能判断是否为引用类型object(不可细分)
手写实现深拷贝:

function deepClone(obj){
    if(typeof obj != 'object' || obj == null){
        return obj;
    }

    let result
    if(obj instanceof Array){
        result = []
    }else{
        result = {}
    }

    for(let key in obj){
        if(obj.hasOwnProperty(key)){
            result[key] = deepClone(obj[key])
        }
    }
    return result;
}

变量计算
1.字符串转换为数字可以用parseInt(),因为在有字符串 + …的式子,会强制把其他变量也转换为字符串
2.‘= =’:能强制转换之后相等的,会判断为true,如:100==‘100’,0==‘ ’,0 = =false ,null = = undifined;那么在什么时候使用= = 呢?一般只有这种情况:
if(obj.a == null) 来判断obj.a是否为null或者undifined。
3.if语句判断的是truely变量(!!a = = true)和false变量(!!a = = false),以下为所有的falsely变量:

!!0 === false;
!!NaN === false;
!!undefined === false;
!!null === false;
!!'' === false;
!!false === false;

判断arr是否是一个数组的方法:
1.Array.isArray(arr)
2.arr instanceof Array

原型和原型链

如何判断一个变量是不是数组?
手写一个简易的Jquery,考虑插件和扩展性
class的原型本质,怎么理解?

以一个class继承的代码为例

class People{
    constructor(name){
        this.name = name;
    }
    eat(){
        console.log(`${this.name} eat rice`);
    }
}

class Student extends People{
    constructor(name,number){
        super(name);
        this.number = number;
    }
    sayHi(){
        console.log(`我叫${this.name},我的学号是${this.number}`);
    }
}

let xiaoming = new Student('小明', 12345);

instanceof:1. 判断new出来的实例对象是属于某一类(xiaoming instance of People,xiaoming instanceof Student, xiaoming instance of Object都为true )
2.[] instanceof Array 判断是否为数组
3.{} instanceof Object 判断是否为对象
4.[] instanceof Object 也会返回true
原型
每个class都有显式原型prototype;
每个实例都有隐式原型__proto__;
实例的__proto__指向对应class的prototype;
原型链
instanceof的作用在这里插入图片描述
当xiaoming instanceof时,就相当于沿着xiaoming的__proto__去查找原型链对应的prototye,如果有那么该prototype对应的类就为true,否则为false.

作用域和闭包

this的不同应用场景,如何取值?
手写bind函数
实际开发中闭包的应用场景,举例说明

自由变量:当前作用域未定义,但使用了。那么就向上级作用域去查找,如果一直找到全局作用域都没找到,那就报错:xxx is not difined.
(闭包中)自由变量的查找

//函数作为返回值
function create(){
    const a = 100;
    return function fn(){
        console.log(a);
    }
}

const re = create();
const a = 200;
re();

// 函数作为传参
function print(fn){
    const a = 200;
    fn();
}

const a = 100;
function fn(){
    console.log(a);
}

print(fn);

结论:自由变量的查找是在函数定义的地方向上级作用域查找,不是在执行的地方

闭包的影响
变量会常驻内存,得不到释放,所以闭包不要乱用,但是闭包并不一定会造成内存泄漏(因为内存泄漏是指垃圾变量没有被释放,而闭包中的变量不一定无用)

this
this取什么值,是在函数执行的时候确认,而不是在函数定义的时候确认。
箭头函数中的this,取它上级作用域的值,它自己没有this
以下是包含this的几种情况


```javascript
//情况一
function fn1(){
    console.log(this);
}

fn1(); //window
fn1.call({x:100}); //{x:100}
fn2 = fn1.bind({x:200});
fn2();//{x:200}

//情况二
const zhangsan = {
    name: '张三',
    sayHi(){
        console.log(this); //zhangsan这个对象
    },
    wait(){
        setTimeout(function(){
            console.log(this)//window
        },1000)
    }
}

//情况三
const lisi = {
    name: '李四',
    sayHi(){
        console.log(this); //lisi这个对象
    },
    wait(){
        setTimeout(() => {console.log(this)},1000)//lisi这个对象
    }
}

this的使用场景及取值:
1.当做普通函数被调用——全局
2.使用call apply bind——指向传入的参数
3.作为对象方法调用——指向上级对象,函数属于谁就指向谁
4.在class的对象中调用——实例本身
5.箭头函数中——找上级作用域中this的值来确定
手写bind函数

Function.prototype.myBind = function(){
    const args = Array.prototype.slice.call(arguments);
    const a = args.shift();
    const self = this;

    return function(){
        return self.call(a, args);
    }
}

异步和单线程

同步和异步的区别是什么?
手写用promise加载一张图片
前端使用异步的场景

JS是单线程语言,只能同时做一件事儿
浏览器和nodejs支持JS启动进程,如web worker
JS和DOM渲染共用同一个线程,因为JS可以修改DOM结构

同步和异步的区别:
1.同步会阻塞代码执行,异步不会阻塞代码执行

前端使用异步的场景:
1.网络请求:如ajax图片加载
2.定时,如setTimeout

手写promise加载图片

const url1 = 'https://www.baidu.com/img/PCfb_5bf082d29588c07f842ccde3f97243ea.png';
const url2 = 'https://pics5.baidu.com/feed/eac4b74543a98226dbbad777b05f51074b90eb4f.jpeg?token=27a371d8135063b0f0b46b46bfe244b6';

function loadImg(src){
    return new Promise((resolve, reject) => {
      const  img  = document.createElement('img');
      img.onload = () =>{
          resolve(img);
      }
      img.onerror = () =>{
          const err = new Error(`图片加载失败${src}`);
          reject(err)
      }
      img.src = src;
    })
}

// loadImg(url1).then((img) =>
//     {console.log(img.width)
//     return img}).then((img) => {
//         console.log(img.height)
//     }).catch((err) => console.error(err))

loadImg(url1).then((img) =>
    {console.log(img.width)
        return img//返回一个普通对象
    }).then((img) => {
        console.log(img.height);
        return loadImg(url2); //返回promise对象
    }).then(img2 =>{
        console.log(img2.width);
        return img2;
    }).then( img2 => {
        console.log(img2.height);
    }).catch((err) => console.error(err))

以下开始过渡到js的web api

DOM(Document Object Model)

DOM是哪种数据结构?
DOM操作常用API
attribute和property的区别
一次性插入多个DOM节点,考虑优化

DOM的本质:从html文件解析出来的树
获取DOM节点的方法:
1.document.getElementById( ) //元素
2.document.getElementsByTagName() //集合
3.document.getElementsByClassName() //集合
4.document.querySelectorAll()//集合

attribute和property的区别:

let plist = document.querySelectorAll('p');
let p = plist[0];

// propety是通过获取属性开改变页面样式的一种形式
p.style.width = '100px';
p.className = 'red';
console.log(p.style.width);
console.log(p.className);
console.log(p.nodeName);
console.log(p.nodeType);

//attribute
p.setAttribute('data-name', 'jiang');
console.log(p.getAttribute('data-name'))
p.setAttribute('style', 'font-size:50px')

1.property:修改对象属性,不会修改到对象属性中;
2.attribute:修改html属性,修改html结构;
3.两者都可能会引起DOM的重新渲染(所以建议使用property)
以上三点为看mooc视频时的总结,根据我自己的理解来讲,我认为这两者之间的区别还在于property只能修改DOM自带的属性,而attribute不仅可以修改dom自带的属性,还可以修改和创建一些自定义属性;

DOM结构操作

const div1 = document.getElementById('div1');
const div2 = document.getElementById('div2');

//创建节点
const newP = document.createElement('p');
newP.innerHTML = 'this is newP';

//插入节点
div1.appendChild(newP);

//移动节点
const p1 = document.getElementById('p1');
div2.appendChild(p1);

//获取父元素
console.log(p1.parentNode);

//获取子元素
console.log(div1.childNodes);
const div1ChildsNodeP = Array.prototype.slice.call(div1.childNodes).filter(child =>{
    if(child.nodeType === 1){
        return true;
    }else{
        return false;
    }
})

console.log(div1ChildsNodeP);//仅为标签的子元素

//删除子元素
div1.removeChild(div1ChildsNodeP[0]);

DOM性能
1.缓存DOM查询结果
2.将频繁操作改成一次操作

const list = document.getElementById('list');

//创建一个文档片段,此时还没有插入DOM中,可以避免多次DOM操作
const frag = document.createDocumentFragment();

for(let i =0;i<10;i++){
    const li = document.createElement('li');
    li.innerHTML = `this is li${i}`;
    frag.appendChild(li);//此处没有操作DOM
}

list.appendChild(frag);//此处操作了一次DOM

DOM的数据结构:树

BOM(Browser Object Model)

如何识别浏览器类型
分析拆解url的各个部分

//navigator
const ua = navigator.userAgent;//拿到浏览器的信息(包括类型)
const isChrome = ua.indexOf('Chrome');
console.log(isChrome)

//screen
console.log(screen.width);
console.log(screen.height);

//location
console.log(location.herf);//取网址
console.log(location.protocol); //取协议(http或https)
console.log(location.host)//取域名
console.log(location.search)//取参数
console.log(location.hash);//取hash
console.log(location.pathname)//取路径

//history
history.back();
history.forward();//网页前进或后退

事件绑定和事件冒泡

编写一个通用的事件监听函数
描述事件冒泡的流程
无限下拉的图片列表,如何监听每个图片的点击

通用的事件监听函数

function bindEvent(item, type,selector,fn){
    if(fn == null){
        fn = selector;
        selector = null
    }
    item.addEventListener(type,event =>{
        let targrt = event.target;
        if(selector){//事件代理绑定
            if(targrt.maches(selector)){
                fn.call(target,event);
            }
        }else{//普通绑定
            fn.call(target,event);
        }
    });
}

ajax

手写一个简易的ajax
跨域的常用实现方式

手写ajax

function ajax({body,headers,url,method}) {
    return new Promise((resolve,reject) =>{
        let request = new XMLHttpRequest();
        request.open(method,url);

        for(let key in headers){
            let value = headers[key];
            request.setRequestHeaders(key,value);
        }
        request.send(body);

        request.onreadystatechange = () =>{
            if(request.readyState === 4){
                if(request.status === 200){
                    resolve(
                        JOSN.parse(request.responseText)
                    )
                }else if(request.status === 400){
                    reject(new Error('request failed'))
                }
            }
        }
    })
}

xhr.readyState:
1.0-(未初始化)还没有调用send()方法;
2.1-(已调用send()方法)还在发送请求;
3.2-(载入完成)send()方法执行完成,已经接收到全部响应内容;
4.3-(交互)正在解析响应内容;
5.4-(完成)响应内容解析完成,可以在客户端调用

xhr.status(状态码):
2xx:表示成功处理请求,如200
3xx:需要重定向,浏览器重新跳转,301(永久重定向),302(临时重定向),304(资源未改变)
4xx:客户端请求错误,404(服务端找不到,请求错误),403(客户端没有权限)
5xx:服务端出错

同源策略:
ajax请求时,浏览器要求当前网页和server必须同源(安全)
同源:协议、域名、端口号三者都相同
img、link、script三个标签不受同源策略限制
img标签可用于统计打点,可使用第三方统计服务
link和script可使用CDN,CDN一般都是外域
script可以实现JSONP,后端也可以根据前端传来的拼接地址进行数据选择来传给前端
所有的跨域,都必须经过server端的允许和配合
cors–设置服务端http header也可以实现跨域
待补充…

axios作为ajax插件,本质是对XMLHttpRequest进行了封装,它支持promise

存储

cookie localstorage sessionstorage的区别
cookie本身用于浏览器和server通讯,可以通过document.cookie修改,也用于本地存储。cookie的值是添加形式的,每次赋值只要没有对cookie中的同一变量就行重赋值,此次赋值不会覆盖之前的值,会直接添加在后面
cookie的特点:
存储大小,最大4KB
http请求时需要发送到服务端,增加请求数据量
只能用document.cookie = ‘…’,太过简陋
localstorage 和sessionstorage
存储大小最大为5M
API简单易用,setItem ,getItem
不会随着http请求被发出去
localstorage数据会永久存储,除非代码手动删除,sessionstorage只存在于当前会话,浏览器关闭则清空。

开发环境

git
抓包
webpack babel
linux的常用命令

git常用命令(按实际项目中使用顺序来写)
git branch(检查当前状况下代码分支)
git checkout -d xxx (创建一个新的分支)
git status(检查当前的修改文件有哪些)
git show(检查修改内容)
git add . xxx(提交修改)
git checkout(撤销上一步修改)
git push origin xxx(推送到服务端)
git pull origin xxx (从服务端下载)
git merge(合并分支)
抓包
chales实现代理网站,手机连接电脑进行抓包,以及下载证书来获取https的信息
webpack和babel
使用原因:
1.ES6模块化,浏览器暂不支持
2.ES6语法,浏览器并不完全支持
3.压缩代码、整合代码,以让网页加载更快

webpack基础配置
1.仅执行打包(webpack.config.js)

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    mode: 'development', //production 开发/线上模式
    entry: path.join(__dirname, 'src', 'index.js'), //找到当前目录下的src的index.js
    output:{
        filename: 'bundle.js',
        path: path.join(__dirname, 'dist')
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.join(__dirname, 'src', 'index.html'),
            filename: 'index.html', //产出文件名,放在dist中
        })
    ],
    devServer:{
        port: 3000,
        contentBase: path.join(__dirname, 'dist')
    }
}

2.将ES6转换为ES5
需要安装babel,配置如下
添加.babelrc文件

{
    "presets": ["@babel/preset-env"]
}

在webpack.config.js中添加

module:{
        rules:[
            {
                test: /\.js$/,
                loader:['babel-loader'],
                include: path.join(__dirname, 'src'),
                exclude: /node_modules/
            }
        ]
    },

3.ES6模块化
如果需要导出多个模块,用export一次性导出,或者挨个导出,然后用解构赋值的方法来获取这些模块
如果只导出一个模块,那就要用export default 但是导出多个的时候如果用了default,就不能再用解构赋值获取了,只能单个导出之后,采用属性的方法来获取模块中的内容

export function fn(){
    console.log(fn);
}

export const age = 10;

export const obj = {
    name: 'zhangsan'
}
function fn(){
    console.log(fn);
}

const age = 10;
const obj = {
    name: 'lisi'
}

export {
    fn,
    age,
    obj,
}
const xxx = {
    nema: 'xxx'
}

export default xxx

4.webpack配置生产环境
需要修改的部分:

mode: 'production', //这里改了
    entry: path.join(__dirname, 'src', 'index.js'), //找到当前目录下的src的index.js
    output:{
        filename: 'bundle.[contenthash].js',//这里改了
        path: path.join(__dirname, 'dist')
    },

有需要的话还可以配置一下package中的build命令
这里在bundle文件名后面加的cotenthash可以随机生成一个乱码,当文件被修改后,它就会改变,有利于性能优化和缓存,后面还会说到
Linux常用命令
ssh 用户名@IP地址 (登录线上机)
ls/li(查看文件夹)
clear (清屏)
mkdir xxx (创建文件夹)
rm -rf xxx (删除文件夹)
cd xxxxxx (进入某目录)
mv xxx yyy (把文件名xxx改为yyy)
mv xxx …/…/…/xxx (修改文件路径)
cp xxx xxx1(把xxx拷贝到xxx1)
touch xxx(新建文件)
vi/vim xxx(新建文件并打开,打开之后可以写入内容,i进入编辑模式,esc后退,:w保存, :q退出,:q!强制退出)
cat xxx(查看文件内容)
grep “关键字” xxx (在文件xxx中查找关键字)

运行环境

网页加载过程
性能优化
安全

从输入url到渲染出页面的整个过程
要加载html代码,媒体文件,如图片、视频等,javajscript css
1.DNS解析:域名 -> ip地址
2.浏览器根据IP地址向服务器发起http请求(包括TCP连接)
3.服务器处理http请求,并返回给浏览器
4.网页根据HTML代码生成DOM Tree,根据CSS代码生成CSSOM
5.将DOM Tree 和CSSOM 整合形成 Render Tree
6.浏览器根据Render Tree渲染页面
7.遇到script标签则暂停渲染,优先加载并执行JS代码,完成再继续
8.直至把 Render Tree渲染完成

为什么要把CSS放在head里,把JS放在body最后呢?
因为CSS里包含的样式最好是在DOM加载时就加载完,然后和DOM Tree一起渲染,否则每次改变样式都造成重新渲染,很麻烦
JS放在不放在最后,会因为JS导致渲染卡住

图片加载不会阻塞渲染

window.onload和DOMContentLoaded的区别:
window.onload页面的全部资源加载完才会执行,包括图片、视频
DOMContentLoaded在DOM渲染完即可执行,此时图片、视频可能还没有加载完

性能优化
1.原则
多使用内存、缓存等方法,减少CPU计算量,减少网络加载耗时(空间换时间)
2.从何入手
一、让加载更快(1)减少资源体积:压缩代码(如webpack打包压缩)
(2)减少访问次数:合并代码。SSR服务器渲染,缓存 (如CSS精灵图、webpack打包时给出口文件名加上contenthash)
(3)使用更快的网络:CDN
二、让渲染更快:(1)css放在head,JS放在body最下面
(2)尽早执行JS,用DOMContentLoaded触发
(3)懒加载(图片懒加载,上滑加载更多)
(4)对DOM查询进行缓存(见前面)
(5)频繁操作DOM,合并到一起插入DOM结构(见前面)
(6)节流throttle,防抖debounce

contenthash的原理:
静态资源加hash后缀,根据文件内容计算hash
文件内容不变,则hash不变,则url不变
Url和文件不变,则会自动触发http缓存机制,返回304

CDN:用来配置静态文件,CDN不变的话也会返回304

SSR:服务端渲染——将网页和数据一起加载,一起渲染
如果是非SSR(前后端分离)——先加载网页,再加载数据,再渲染数据

懒加载(待补充)
给图片的src设置为preview.png,然后将真正的图片地址赋值给一个自定义属性,当屏幕滑动到图片的位置时,再显示图片

防抖debounce
监听一个输入框,文字变化后触发change事件
直接用keyup事件,则会频繁触发chenge事件
防抖:用户输入结束或暂停时,才会触发change事件
手写防抖:

function debounce (fn, delay = 500){
    let timer = null;
    return function (){
        if(timer){
            clearTimeout(timer);
        }
        timer = setTimeout(() =>{
            fn(this,arguments)
            timer = null;
        },delay)
    }
}

input1.addEventListener('keyup', debounce(() => {
    console.log(input1.value)
}),600)

节流throttle(用于比防抖更频繁的触发)
拖拽一个元素时,要随时拿到该元素被拖拽的 位置
直接用drag事件,则会频繁触发,很容易导致卡顿
节流:无论拖拽速度多快,都会每隔一定时间触发一次

function throttle(fn,delay){
    let timer = null;
    return function(){
        if(timer){
            return
        }
        timer = setTimeout(() =>{
            fn.apply(this,arguments);
            timer  = null;
        },delay)
    }
}

安全

常见的web前端攻击方式有哪些?
XSS跨站脚本攻击
CSRF 跨站请求伪造

XSS跨站脚本攻击
如:一个网站,我在其中写了一个script标签,里面包含获取用户cookie的代码,当用户点入网站时,即可获取用户的cookie,发送到服务端

XSS预防:
1.替换特殊字符,如<变为< >变为>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值