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

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

作者: 冰淇wbq | 来源:发表于2018-12-09 17:39 被阅读0次

    变量类型

    • 值类型和引用类型
    • 值类型只能存储一个值
    • 值类型复制就是值类型本身
    • 引用类型复制只是复制引用类型的指针
    • 引用类型:对象,数组,函数
    • 引用类型特点:可以无限制的扩展属性
    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>
      • 当查看邮件时就已完成付费了

      解决方法:

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

    相关文章

      网友评论

          本文标题:慕课网《前端JavaScript基础面试技巧》学习笔记

          本文链接:https://www.haomeiwen.com/subject/aedkhqtx.html