慕课网《前端JavaScript基础面试技巧》学习笔记

变量类型

  • 值类型和引用类型
  • 值类型只能存储一个值
  • 值类型复制就是值类型本身
  • 引用类型复制只是复制引用类型的指针
  • 引用类型:对象,数组,函数
  • 引用类型特点:可以无限制的扩展属性
  1. JS中使用typeof能得到哪些类型:7种类型
  • undefined string number boolean symbol object function
  • typeof不能详细区分引用类型(对象、数组)的详细类型
  • 但是可以详细区分function以及所有值类型。
typeof a//undefined
typeof undefined//undefined
typeof 'abc' //string
typeof 123 //number
typeof NaN //number
typeof true //boolean
typeof {} //object
typeof [] //object
typeof null //object
typeof console.log //function
typeof Symbol('foo') //symbol

变量计算-强制类型转换

  • 字符串拼接
let a=100;
let b=10;
console.log(a+b);//110

let a=100;
let b='10';
console.log(a+b);//10010
console.log(a-b);//90
  • ==运算符
100=='100'//true
0==''//true(0和''都可以转换成false)
null==undefined//true(null和undefined都可以转换成false)
  • if语句
let a=true
if(a){
    console.log(a);//true
}
let b=100;
if(b){
    console.log(b);//100
}
let c='';
if(c){
console.log('c')
}
//c=''是false,if语句不执行
  • 逻辑运算
console.log(10&&0);//0
console.log(''||'abc');//abc
console.log(!window.abc);//true
//判断一个变量会被当做true还是false
var a=100;
console.log(!!a);//true
  1. 何时使用===,何时使用 ==
//仅有这种情况使用'=='
if(obj.a==null){
  //此时条件相当于obj.a===null||obj.a===undefined,简写形式
  //这是jQuery源码中推荐的写法
}

除此之外,其它情况均建议使用'==='

  1. JS有哪些内置函数
  • js内置函数是浏览器内核自带的,不用任何函数库引入就可以直接使用的函数。
  • Object,Array,Boolean,Number,String,Function,Date,RegExp,Error
  • Math是内置对象
  • Math内置对象常用方法
    • Math.floor():向下取整
    • Math.ceil():向上取整
    • Math.round():四舍五入
    • Math.max(a,b):取a,b之间最大值
    • Math.min(a,b):取a,b之间最小值
    • Math.random:取0-1之前的随机数
let a=4.3;
console.log(Math.round(a));
console.log(Math.ceil(a));
console.log(Math.floor(a));
console.log(Math.max(5,3));
console.log(Math.min(1,4));
function random(min,max) {
    return min+Math.random()*(max-min)
}
console.log(random(1,10));
  1. JS变量按照存储方式分为哪些类型,并描述其特点
  • 值类型和引用类
  • 值类型可以将数据分块存储在内存中
  • 引用类型是多个变量共用一个内存块,引用类型的赋值是指定了一个指针,并不是真正的值的拷贝,它们之间是会相互干预的。
  1. 如何理解JSON
  • JSON是JS中的一个内置对象
JSON.stringify({a:10,b:20}) //"{"a":10,"b":20}"将对象转换为字符串
JSON.parse('{"a":10,"b":20}') //{a: 10, b: 20}把字符串转换为对象

原型和原型链

  • 构造函数
function Foo(name,age) {
    this.name=name;
    this.age=age;
    this.class='class-1';
//    return this;//默认有这一行
}
var f=new Foo('wbq',22);
  • 构造函数-扩展
var a={};//其实是var a=new Object()的语法糖
var b=[];//其实是var b=new Array()的语法糖
function Foo() {};//其实是var Foo=new Function(){}的语法糖
  • 原型规则和示例
    • 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(除了'null')
    • 所有的引用类型(数组、对象、函数),都具有proto属性,属性值是一个普通的对象
    • 所有的函数,都有一个prototype属性,属性值也是一个普通对象
    • 所有的引用类型(数组、对象、函数),proto属性值指向它的构造函数的prototype属性
    • 当试图得到一个对象的某个属性时,如果对象本身没有这个属性,那么会去它的proto(即它的构造函数的prototype)中寻找
var obj={};obj.a=100;
var arr=[];arr.a=100;
function fn(){};fn.a=100
console.log(obj.__proto__);//{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
console.log(arr.__proto__);//[constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]
console.log(fn.__proto__);//ƒ () { [native code] }
console.log(fn.prototype);//{constructor: ƒ}
console.log(obj.__proto__===Object.prototype);//true

function Foo(name,age) {
    this.name=name;
    this.age=age;
}
Foo.prototype.alertName=function () {
    alert(this.name)
}
var f=new Foo('wbq');
f.printName=function () {
    console.log(this.name)
}
f.printName();
f.alertName();
  • 循环对象自身的属性方法
  1. for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)
function Foo(name,age) {
    this.name=name;
    this.age=age;
}
Foo.prototype.alertName=function () {
    alert(this.name)
}
var f=new Foo('wbq');
f.printName=function () {
    console.log(this.name)
}
f.printName();
f.alertName();
for(let i in f){
    console.log(i);//name,age,printName,alertName
}
//如何只遍历对象自身的属性,不遍历继承的可枚举属性
for(let i in f){
    if(f.hasOwnProperty(i)){
        console.log(i,f[i])
    }
}
  1. Object.keys(obj)
  • Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。

  • 大多数时候,我们只关心对象自身的属性。所以,尽量不要用for...in循环,而用Object.keys()代替。

  • Object.values(obj):方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。

  • bject.entries():方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。

console.log(Object.keys(f));// ["name", "age", "printName"]

let obj={
    'name':'wbq',
    'age':20,
    sayName(){
        console.log(this.name)
    }
}
console.log(Object.keys(obj));//[ 'name', 'age', 'sayName' ]
console.log(Object.values(obj));//[ 'wbq', 20, [Function: sayName] ]


console.log(Object.entries(obj));
// [ [ 'name', 'wbq' ],
//     [ 'age', 20 ],
//     [ 'sayName', [Function: sayName] ] ]
  1. Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。

  1. Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。

  1. Reflect.ownKeys(obj)

Reflect.ownKeys返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。

  • 原型链

如果对象本身没有某个属性,则通过proto向上查找,最终都没有这个属性,则返回null,这种通过proto一级一级向上查找,则形成了原型链

  • instanceof
    • 用于判断引用类型属性是哪个构造函数的方法
    • f instanceof Foo的判断逻辑是:
    • f的proto一层一层往上,能否对应到Foo.prototype
  1. 如何准确判断一个变量是数组类型
var arr=[]
arr instanceof Array    //true
typeof arr    //object    typeof无法准确判断是否是数组
  1. 写一个原型链继承的例子
    function Animal(name){
        this.name=name
    }
    Animal.prototype.run=function(){
        console.log('run');
    };
    function Dog(name) {
        Animal.call(this,name)
    }
    Dog.prototype=new Animal();
    let dog=new Dog('wbq');
    dog.run();
    console.log(dog.name)
  • 封装一个DOM查询
<div id="div1">ddd</div>

function Elem(id) {
    this.elem=document.getElementById(id);
}
Elem.prototype.html=function (val) {
    var elem=this.elem;
    if(val){
        elem.innerHTML=val;
        return this
    }else{
        return elem.innerHTML
    }
}
Elem.prototype.on=function (type,fn) {
    var elem=this.elem;
    elem.addEventListener(type,fn)
    return this
}
var div1=new Elem('div1');
div1.on('click',function () {
    alert('click')
}).html('<p>链式操作</p>')
  1. 描述new一个对象的过程
//创建一个构造函数
function Foo(name,age){
 // this={}
    this.name=name
    this.age=age
 // return this
}
var f=new Foo('wbq',10);
  • 创建一个新对象:{}
  • 将构造函数的作用域赋给新对象(因此this指向这个新对象):this={}
  • 执行构造函数中的代码(为这个新对象添加属性):this.xxx=xxx
  • 返回新对象:return this
let newFun=function (func) {
    //1.新建一个空对象,并将 构造函数的原型对象赋给这个空对象
    let obj=Object.create(func.prototype);
    //2.执行构造函数,相应参数被传入,并将this的上下文指向新创建的对象obj
    var ret=func.call(obj);
    //3.如果构造函数返回了对象,就舍弃之前创建的对象obj,newObj = ret
    if(typeof ret === 'object') return ret;
    else return obj;
}
var foo = function(name){
    this.name = name || 'lalaBao';
}
var newObj = newFun(foo);
console.log(newObj);

作用域和闭包

  • 执行上下文
    • 范围:一段<script>或者一个函数
    • 全局:变量定义、函数声明 一段<script>
    • 函数:变量定义、函数声明、this、arguments
  • this
    • this要在执行时才能确认值,定义时无法确认
    var a={
        name:'A',
        fn:function () {
            console.log(this.name)
        }
    }
    a.fn();//A  this===a
    var fn1=a.fn;
    a.fn.call({name:'B'});//B  this==={name:'B'}
    console.log(fn1());//undefined  this===window
    
  • 闭包
    • 使用场景
    • 返回一个函数,函数作为返回值
    function F1() {
    var a=100;
    //返回一个函数,函数作为返回值
    return function () {
        console.log(a);//自由变量,去父作用域寻找
        }
    }
    var f1=F1()
    var a=200
    f1();//100
    
    • 函数作为参数来传递
    function F1() {
        var a=100;
        return function () {
            console.log(a)
        }
    }
    var f1=F1()
    function F2(fn) {
        var a=200
        fn()
    }
    F2(f1);//100
    
  1. 说一下对变量提升的理解
  • 在整个js代码执行前,会先声明带var和带function关键字的变量
  • 带var关键字的变量,只是被提前声明,不会被赋值
  • 带function关键字的变量,被提前声明,也会给这个函数名赋值
console.log(a);
let a=10;//Uncaught ReferenceError: a is not defined
var a;//undefined

fn('wbq');
function fn(name) {
    console.log(name);//wbq
}
  1. 说明this几种不同的使用场景
  • 作为构造函数执行
functon Foo(name){
    this.name=name;
}
var f=new Foo('wbq')
  • 作为对象属性执行
var obj={
    name:'wbq',
    printName:function(){
        console.log(this.name)
    }
}
obj.printName();
  • 作为普通函数执行
function fn(){
    console.log(this);
}
fn();
  • call,apply,bind
function fn1(name,age){
    console.log(name);
    console.log(this);
}
fn1('wbq');
fn1.call({x:100},'zhangsan',20);
fn1.apply({x:100},['zhangsan',20]);
 //bind在函数声明的形式后不可用,必须是函数表达式
var fn2=function (name,age){
    console.log(name);
    console.log(this);
}.bind({y:200});
fn2('zhangsan',20)
  • bind是ES5新出的方法,有些浏览器不兼容
  • 用原生js改写bind
    Function.prototype.abind = function(context){
        self = this;  //保存this,即调用bind方法的目标函数
        return function(){
            return self.apply(context,arguments);
        };
    };
    
  1. 创建10个<a>标签,点击时弹出对应序号

错误写法

  • 当点击事件发生时,for循环已经执行完毕
  • 这时的i已经是循环后的值10
  • 所以点击后都弹出10
//使用let形成一个块级作用域
for(let i=0;i<10;i++){
    var a=document.createElement('a');
    a.innerHTML=i;
    a.onclick=function () {
        alert(i)
    };
    document.body.appendChild(a);
}
//使用闭包
定义自执行函数,就是不同调用,只要定义完成,立即执行的函数
for(var i=0;i<10;i++){
    var a=document.createElement('a');
    a.innerHTML=i;
    (function (i) {
        a.onclick=function () {
            alert(i)
        };
    })(i);

    document.body.appendChild(a);
}
  1. 如何理解作用域
    JavaScript的作用域和作用域链
  • JavaScript的作用域指的是变量的作用范围
  • 内部作用域由函数的形参,实参,局部变量,函数构成
  • 内部作用域和外部的作用域一层层的链接起来形成作用域链
  • 当在在函数内部要访问一个变量的时候,首先查找自己的内部作用域有没有这个变量,如果没有就到这个对象的原型对象中去查找,还是没有的话,就到该作用域所在的作用域中找,直到到window所在的作用域,每个函数在声明的时候就默认有一个外部作用域的存在了
①没有块级作用域
    if(true){
        var name='zhangsan'
    }
    console.log(name)//'zhangsan'
②只有全局和函数作用域
    var a=100;
    function fn(){
        var a=200;
        console.log('fn',a)
    }
    console.log('global',a)
    fn()
 
  1. 实际开发中闭包的应用
  • 闭包简单的说就是一个函数能访问外部函数的变量,这就是闭包
/闭包实际应用中主要用于封装变量,收敛权限
      function isFirstLoad(){
             var _list=[];
              return function(id){
                 if(_list.indexOf(id)>=0){
                     return false;
                 }else{
                    _list.push(id);
                    return true;
                 }
              }
      }
      //使用
      var firstLoad=isFirstLoad();
      firstLoad(10);//true
      firstLoad(10);//false
    //你在 isFirstLoad 函数外面,根本不可能修改掉_list的值

异步和单线程

  1. 同步和异步的区别是什么?分别举一个同步和异步的例子
  • 同步与异步最大的区别是阻塞代码,同步会阻塞代码,而异步不会
  • alert是同步,setTimeout是异步
//同步
console.log(100);
alert(200)
console.log(300);
//异步
console.log(100);
setTimeout(function(){
    console.log(200);
},1000)
console.log(300);
  • 执行第一行,打印100
  • 执行setTimeout后,传入setTimeout的函数会被暂存起来,不会立即执行(单线程的特点,不能同时执行两个任务)
  • 执行最后一行,打印300
  • 待所有任务执行完,处于空闲状态,才执行暂存的任务
  • 暂存的setTimeout无等待时间,立即执行
  1. 前端使用异步的场景有哪些
  • 定时任务:setTimeout、setInterval
  • 网络请求:ajax请求,fetch(),动态img加载
  • 事件绑定

它们共同的特点是需要等待,由于js是一个单线程语言,为了避免阻塞,所以要使用异步

//fetch
    console.log('start');
    fetch("./test-test.json").then(res=>res.json())
        .then(data=>{
            console.log(data)
        })
        .catch(error=>{
        console.log('error')
    })
    console.log('end');
//img loaded    
    console.log('start');
    var img=document.createElement('img');
    img.onload=function () {
        console.log('loaded')
    };
    img.src='https://box.bdimg.com/static/fisp_static/common/img/searchbox/logo_news_276_88_1f9876a.png'
    console.log('end');
    document.body.appendChild(img);
//事件    
    <button id="btn">点击</button>
    console.log('start');
    var btn=document.getElementById('btn');
    btn.addEventListener('click',function () {
        console.log('clicked')
    })
    console.log('end');

其它知识点

  • 日期
    • Date.now();//1543806595543,获取当前时间的毫秒数
    • var dt=new Date();//2018-12-03T03:09:55.536Z,获取Date对象
    • dt.getTime();//1543806641907,获取毫秒数
    • dt.getFullYear();//2018,年
    • dt.getMonth();//11,月(0-11)
    • dt.getDate();//3,日(0-31)
    • dt.getHours();//11, 小时(0-23)
    • dt.geetMinutes();//13,分钟(0-59)
    • dt.getSeconds();//20,秒(0-59)
  • 数组
    • forEach:遍历所有元素
    • every:判断所有元素是否都符合条件
    • some:判断是否有至少一个元素符合条件
    • sort:排序
    • map:对元素重新组装,生成新数组
    • filter:过滤符合条件的元素
    let arr=[1,2,3,4];
    arr.forEach((item,index)=>console.log(item,index));//1,0 2,1 3,2 4,3
    
    let arr=[1,2,3,4];
    arr.every((item,index)=>console.log(item>0))//true
    
    let arr=[1,2,3,4];
    arr.some((item,index)=>console.log(item>1));//false true true true
    
    let arr=[42,3,1,2,63,7,18];
    console.log(arr.sort((a,b)=>a-b));//[ 1, 2, 3, 7, 18, 42, 63 ]
    
    let arr=[42,3,1,2,63,7,18];
    console.log(arr.map((item)=>item*3));//[ 126, 9, 3, 6, 189, 21, 54 ]
    
    let arr=[42,3,1,2,63,7,18];
    console.log(arr.filter((item)=>item>38));//[ 42, 63 ]
    
  • 对象API
    • for...in
  1. 获取2017-07-13格式的日期
    function formatDate(dt) {
        if (!dt) {
            dt = new Date();
        }
        var year = dt.getFullYear()
        var month = dt.getMonth() + 1
        var date = dt.getDate()
        if (month < 10) {
            month = '0' + month
        }
        if (date < 10) {
            date = '0' + date
        }
        return year + '-' + month + '-' + date
    }
    var dt
    dt = new Date()
    alert(formatDate(dt))
    
    function getYearMonthDate(dt=new Date()) {
    let year=dt.getFullYear();
    let month=dt.getMonth()+1;
    let date=dt.getDate();
    if(month<10){
        month='0'+month
    }
    if(date<10){
        date='0'+date
    }
    return `${year}-${month}-${date}`
}
console.log(getYearMonthDate());

  1. 获取随机数,要求是长度一致的字符串格式
    var random = Math.random()
    random = random + '0000000000' //10个0
    random=random.slice(0,10) //slice() 方法返回一个从0开始到1结束(不包括结束)选择的数组的一部分,浅拷贝到一个新数组对象。原始数组不会被修改
    console.log(random);
  1. 写一个能遍历对象和数组的通用forEach函数
    function myForEach(obj, fn) {
        var key
        if (obj instanceof Array) { //判断是否为数组
            obj.forEach(function (item, index) {
                fn(index, item)
            })
        } else { //不是数组就是对象
            for (key in obj) {
                fn(key, obj[key])
            }
        }
    }
    var arr = [1, 2, 3] //参数顺序换了,为了和对象的遍历格式一致
    myForEach(arr, function (index, item) {
        console.log(index, item);
    })
    var obj = {x: 100, y: 200}
    myForEach(obj, function (key, value) {
        console.log(key, value);
    }) 

DOM和BOM

  1. DOM是哪种基本的数据结构:树
  2. DOM操作常用的API有哪些
  • 创建节点:createElement
  • 添加节点:appendChild
  • 获取父元素:parentElement
  • 获取子元素:childNodes
  • 移除子元素:removeChild
  • 设置属性:setAttribute
  • 获取属性:getAttribute
  1. DOM节点的attr和property有何区别
  • property是一个JS对象的属性的修改
  • Attribute是HTML标签属性的修改
  1. 如何检测浏览器的类型:navigator.userAgent
var ua=navigator.userAgent
var isChrome=ua.indexOf('Chrome')
console.log(isChrome);
  1. 拆解url的各部分
//location
console.log(location.href);
console.log(location.protocol); //协议 http https 
console.log(location.pathname); //域名之后的路径 
console.log(location.search);
console.log(location.hash);

事件

  1. 编写一个通用的事件监听函数
function bindEvent(elem,type,fn) {
    elem.addEventListener(type,fn)
}
var a=document.getElementById('link1');
bindEvent(a,'click',function (e) {
    e.preventDefault();//阻止默认行为
    console.log('clicked')
});
  1. 描述事件冒泡流程
  • DOM树形结构
  • 事件冒泡
  • 阻止冒泡:e.stopPropagation()
  1. 对于一个无限下拉加载图片的页面,如何给每个图片绑定事件

利用事件委托,事件都有事件冒泡机制,给父级元素绑定事件,通过e.target找到事件源

  • 代理的两个优点
    • 代码简洁
    • 减少浏览器内存占用
var oUl=document.getElementsByTagName('ul')[0];
oUl.addEventListener('click',function (e) {
    let target=e.target;
    if(target.nodeName=='LI'){
        console.log(target.innerHTML)
    }
});

存储与Ajax

  1. 请描述一下cookie,sessionStorage,和localStorage的区别
  • 容量区别,cookie为4k,localStorage和sessionStorage为5M
  • cookie每次请求都会被携带在ajax中,
  • localStorage和sessionStorage不会被携带只作为存储使用
  • API易用性
  1. 手动编写一个ajax,不依赖第三方库
var xhr=new XMLHttpRequest()
xhr.open('GET','/api',false)
xhr.onreadystatechange=function(){ //这里的函数异步执行 if(xhr.readyState==4){
    if(xhr.status==200){
        alert(xhr.responseText)
        }
    } 
}

xhr.send(null)
  1. 跨域的几种实现方式

    浏览器有同源策略,不允许ajax访问其他域接口

    跨域条件:协议、域名、端口,有一个不同就算跨域

  • JSONP
  • 服务器端设置http header:CORS(跨域资源共享)
  • proxy代理模式
devServer: {
    historyApiFallback: true,
    noInfo: true,
    overlay: true,
    proxy:{
        "/api/":{
            target:"http://testmove.kandayi.com.cn/",
            changeOrigin:true
        }
    }
  },

模块化

模块化本身就是一个面试的问题

  • 不会模块化代码写法
  • 这些代码中的函数必须是全局变量,才能暴露给使用方。容易造成变量污染
  • a.js知道要引用a-util.js,但是他知道还需要依赖于util.js吗
//util.js
function getFormatDate(dt=new Date(),type=1) {
    let year=dt.getFullYear();
    let month=dt.getMonth()+1;
    let date=dt.getDate();
    if(type==1){
        if(month<10){
            month='0'+month
        }
        if(date<10){
            date='0'+date
        }
        return `
        ${year}-${month}-${date}
    `
    }else{
        return `
        ${year}年${month}月${date}日
    `
    }
}
//a-util.js
function aGetFormatDate(date) {
    return getFormatDate(date,2)
}
//a.js
var dt=new Date();
console.log(aGetFormatDate(dt));
//index.html
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script src="util.js"></script>
<script src="a-util.js"></script>
<script src="a.js"></script>
<script>

</script>
</body>
</html>
  • 使用模块化解决以上问题
  • 直接<script data-main="./main.js"></script>,其他的根据依赖关系自动引用
  • 那两个函数,没必要做成全局变量,不会带来污染和覆盖
//util.js
define(function () {
    return {
        getFormatDate:function (date,type) {
            let year=date.getFullYear();
            let month=date.getMonth()+1;
            let day=date.getDate();
            if(type==1){
                if(month<10){
                    month='0'+month
                }
                if(day<10){
                    day='0'+day
                }
                return `
        ${year}-${month}-${day}
    `
            }else{
                return `
        ${year}年${month}月${day}日
    `
            }
        }
    }
})
//a-util.js
define(['./util.js'],function (util) {
    return {
        aGetFormatDate:function (date) {
            return util.getFormatDate(date,2)
        }
    }
})
//a.js
define(['./a-util.js'],function (aUtil) {
    return {
        printDate:function (date) {
            console.log(aUtil.aGetFormatDate(date))
        }
    }
})
//main.js
require(['./a.js'],function (a) {
    var date=new Date();
    a.printDate(date);
});
//test.html
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script src="https://cdn.bootcss.com/require.js/2.3.6/require.min.js" data-main="./main.js"></script>
<script>

</script>
</body>
</html>

  • AMD
    • A:异步 M:模块 D:定义
    • require.js <a>requirejs.org</a>
    • 全局define函数
    • 全局require函数
    • 依赖JS会自动异步加载
    • 使用requirejs完成刚才的例子
  • CommonJS
    • CommonJS是Nodejs模块化规范,现在被大量用前端
    • 前端开发依赖的插件和库,都可以从npm获取
    • 构建工具高度自动化,使npm成本非常低
    • CommonJS本身不会异步加载JS,而是一次性同步加载出来
    • module.exports={aaa:...,bbb:...}输出模块,require(xxx.js)引用模块
  • AMD和CommonJS的使用场景
    • 需要异步加载,使用AMD
    • 使用npm后建议使用CommonJS(webpack,Node.js)
//a-util.js
var getFormatDate=require('util');
module.exports={
    aGetFormatDate:function(date) {
        return getFormatDate(date,2)
    }
}

//util.js
module.exports={
    getFormatDate:function (dt=new Date(),type=1){
        let year=dt.getFullYear();
        let month=dt.getMonth()+1;
        let date=dt.getDate();
        if(type==1){
            if(month<10){
                month='0'+month
            }
            if(date<10){
                date='0'+date
            }
            return `
        ${year}-${month}-${date}
    `
        }else{
            return `
        ${year}年${month}月${date}日
    `
        }
    }
}

构建工具

  • grunt
  • gulp
  • fis3:百度内部
  • webpack
    • new webpack.optimize.UglifyJsPlugin():js压缩

常用Git命令

  • git add .:添加所有文件
  • git checkout xxx:还原某个文件
  • git commit -m 'xxx':提交文件到本地仓库
  • git push origin master:提交文件到远程仓库
  • git pull origin master:拉取远程仓库文件到本地
  • git branch:看当前分支
  • git checkout -b xxx:新建分支
  • git checkout xxx:切换到某个分支
  • git merge xxx:合并分支
  • git status:查看状态
  • git clone:拷贝线上项目地
  • cat README.md:查看文件
  • vi README.md:往文件里插入内容
  • git diff:查看文件修改内容
https://git.coding.net/limiywbq/test.git
mkdir js-git-test
cd js-git-test
git init
echo "# js-git-test" >>README.md
git add README.md
cat README.md
git status
git commit -m 'first commit'
git push origin master

上线流程要点

  • 将测试完成的代码提交到git版本库的master分支
  • 将当前服务器的代码全部打包并记录版本号,备份
  • 将master分支的代码提交覆盖到线上服务器,生成新版本号

回滚流程要点

  • 将当前服务器代码打包并记录版本号,备份
  • 将备份的上一个版本号解压,覆盖到线上服务器,并生成新的版本号

linux基本命令

mkdir a:创建文件夹
ls:查看文件夹的名字
ll:查看文件夹下的内容
cd a:进入a文件夹
pwd:查看文件夹所有文件路径
cd ..:返回上一级文件夹
rm -rf a:删除文件夹a
vi a.js:创建编辑a.js
i:在文件里输入内容
ESC:wq:退出并保存文件
cat a.js:查看文件全部内容
rm a.js:删除文件

页面加载与性能优化

  • 加载资源的形式

    • 输入url(或跳转页面)加载HTML
    • 加载HTML中的静态资源 script link img等
  • 加载一个资源的过程

    • 浏览器根据DNS服务器得到域名的IP地址
    • 向这个IP的机器发送http请求
    • 服务器收到、处理并返回http请求
    • 浏览器得到返回内容
  • 浏览器渲染页面的过程

    • 根据HTML结构生成DOM Tree
    • 根据CSS生成CSSOM
    • 将DOM和CSSOM整合成RenderTree
    • 根据RenderTree渲染和展示
    • 遇到<script>时,会执行并阻塞渲染,所以<script>放在<body>即将结束的位置
    • 因为js有权利改变dom结构,如果同时进行会发生冲突
  1. 从输入url到得到HTML的详细过程
    • 浏览器根据DNS服务器得到域名的IP地址
    • 向这个IP的机器发送http请求
    • 服务器收到、处理并返回http请求
    • 浏览器得到返回内容
  2. window.onload和DOMContentLoaded的区别
  • window.onload,页面的全部资源加载完成才会执行,包括图片视频等
  • DOMContentLoaded,DOM渲染完即可执行,此时图片视频等可能还没加载完
window.addEventListener('load',function(){
//页面的全部资源加载完成才会执行,包括图片视频等
})
document.addEventListener('DOMContentLoaded',function(){
//DOM渲染完即可执行,此时图片视频等可能还没加载完
})

  1. 性能优化

原则:

  • 多使用内存,缓存或者其他方法
  • 减少CPU计算,减少网络
从哪里入手
  • 加载页面和静态资源
  • 页面渲染
    • 静态资源的压缩合并
    • 静态资源缓存
    • 使用CDN让资源加载更快
    • 使用SSR后端渲染,数据直接输出到HTML中
性能优化的几个示例
  • CSS放前面,JS放后面
  • 合并js文件,减少请求次数(如webpack的plugins:[new webpack.optimize.UglifyJsPlugin()])
  • 通过连接名称控制缓存<script src="abc_1.js"></script>只有内容改变时才更改名称<script src="abc_2.js"></script>
  • 使用CDN<scriptsrc="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"></script>
  • 使用SSR后端渲染(Vue、React):可以把后端数据直接渲染到页面中,不必要通过ajax调取后端数据,可以很大程度提高页面性能
  • 懒加载
<img id="img1" src="preview.png" data-realsrc="abc.png"/>
<script>
var img1=document.getElementById('img1');
img1.src=img1.getAttribute('data-realsrc');
</script>
  • 减少DOM查询,对DOM查询做缓存
//未缓存DOM查询
var i
for(i=0;i<document.getElementsByTagName('p').length;i++){
    //todo
}
//缓存了DOM查询
var pList=document.getElementsByTagName('p')
var i
for(i=0;i<pList.length;i++){
    //todo
}

这样可以避免多次执行DOM查询

  • 合并DOM插入
var listNode=document.getElementById('list')
//插入10个li标签 
var frag=document.createDocumentFragment(); 
var x,li 
for(x=0;x<10;x++){         li=document.createElement('li')
li.innerHTML='List item'+x frag.appendChild(li) } 
listNode.appendChild(frag)

安全性与面试技巧

  • XSS跨站请求攻击

    • 写一篇文章,同时偷偷插入一段<script>
    • 攻击代码中,获取cookie,发送自已的服务器
    • 发布文章被人查看
    • 会把查看者的cookie发送到攻击者的服务器

    解决方法:

    • 前端替换关键字,如替换<为&lt,替换>为&gt
    • 后端替换
  • XSRF跨站请求伪造

    • 已登录一个网站正在浏览商品
    • 该网站付费接口是xxx.com/pay?id=100但是没有任何验证,
    • 然后你收到了一封邮件,隐藏<img src=xxx.com/pay?id=100>
    • 当查看邮件时就已完成付费了

    解决方法:

    增加验证流程,如输入指纹,密码,短信验证码

推荐阅读更多精彩内容