3

javascript

作为junior developer,仅仅觉得这些问题经常在我面试的时候被问到,好记性不如烂笔头,自己整理记录一遍~~~

1.javascript如何实现继承?

三种方式:

 1. 原型继承

            // 优点:既继承了父类的模板,又继承了父类的原型对象
             // 缺点:父类实例化传参,而不是子类实例化传参(不符合常规语言的写法)
            
            function Parent(work, drink) {
                this.work = function() {
                    console.log(work);
                }
                
                this.drink = drink;
            }
            
            Parent.prototype.draw = function() {
                alert("I can draw");    
                
            }
            
            function Child() {
                this.cry = function() {
                    console.log("the best ability is to cry"); 
                }
            }
            
            Child.prototype = new Parent('code', 'beer');
            var xiaoLi = new Child();
            xiaoLi.work(); // code
            xiaoLi.draw(); // I can draw
            xiaoLi.cry();  // the best ability is to cry

    关于原型链的解释:https://www.cnblogs.com/chengzp/p/prototype.html
 2. 类继承(借用构造函数的方式继承)

            // 优点:继承了父类的模板,方便通过子类实例化传参
            // 缺点:不能继承父类的原型对象
            function Parent(work, drink) {
                this.work = function() {
                    console.log(work);
                }
                
                this.drink = drink;
            }
            Parent.prototype.draw = function() {
                alert("I can draw");    
                
            }
            
            
            function Child(work, drink, sex) {
                Parent.call(this, work, drink);
                this.sex = sex;
                
            }
            
            var xiaoLi = new Child('code', 'beer', 'male');
            alert(xiaoLi.drink); // code
            xiaoLi.work(); // beer
            // xiaoLi.draw(); //没有继承父类的原型对象,所以,会报错:xiaoLi.draw() is not a function
            console.log(xiaoLi.sex); // male
 3. 混合继承(原型继承和类继承)

            // 混合继承(原型继承和类继承(借用构造函数的方式继承))
            function Parent(eat, sleep) {
                this.eat = function() {
                    console.log("function 1" + eat);
                }
                this.sleep = function() {
                    console.log("function 2" + sleep);
                }
            }
            
            Parent.prototype.other = "work";
            
            function Child(eat, sleep, age) {
                Parent.call(this, eat, sleep);
                this.age = age;
            }
            
            Child.prototype = new Parent();
            var xiaoLi = new Child("cake", "want to sleep", "10");
            xiaoLi.eat();
            xiaoLi.sleep();
            console.log(xiaoLi.age);
            console.log(xiaoLi.other);
2.原生ajax是如何实现的?

特点:
在不重新加载整个网页的情况下,对页面局部进行刷新。
XMLHttpRequest对象是实现ajax的基础,XMLHttpRequest有很多方法,常用的有open(),send()等
ajax请求共包含五个步骤:
1.创建XMLHttpRequest对象(一个异步调用对象)
2.设置HTTP请求的参数(请求方法,url,是否异步)
3.设置响应HTTP请求状态变化的回调函数
4.发送HTTP请求
5.获取异步调用返回的数据
6.局部更新页面
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function () {
    if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304)) {
        fn.call(this, xhr.responseText);
    }
};
xhr.send(data);
post方法一定要用setRequestHeader("header","value");
3.作用域
块级作用域:ES6才支持,在{}大括号内的就是块级作用域
(块级作用域的出现解决了:for循环定义的变量造成的全局污染;不用再通过闭包来保存必要的变量了)
函数作用域:在函数中实现的
全局作用域:在外部声明的,如果没有用var关键字声明,在非严格模式下,也为全局作用域

注意:
在es6之前没有块级作用域这个概念,所以在{}块级作用域中声明的变量其实就是全局变量(举个栗子)
    for (var i = 1; i <= 10; i++) {
        console.log (i); // outputs 1, 2, 3, 4, 5, 6, 7, 8, 9, 10;​
    };
    ​
    ​// The variable i is a global variable and it is accessible in the following function with the last value it was assigned above ​
    ​function aNumber () {
    console.log(i);
    }
    ​
    ​// The variable i in the aNumber function below is the global variable i that was changed in the for loop above. Its last value was 11, set just before the for loop exited:​
    aNumber ();  // 11​
    
    关于es6的let和const:
    let和const没有变量声明的提升;
    let和const不允许在相同作用域内重复声明;
4.如何理解闭包?
比较常用的情景:
在A函数里返回B函数,B函数可以调用A函数的局部变量;
特点:
1.函数嵌套
2.函数内部可以引用外部的参数和变量
3.闭包函数里的参数和变量不会被垃圾回收机制回收(闭包会使变量始终保存在内存中,如果使用不当会增大内存消耗)
闭包的好处:
1.希望变量长期保存在内存中
2.避免全局变量的污染
3.私有变量的存在
5.回调函数
定义:
函数A作为参数传递给函数B,在函数B中执行函数A,此时函数A就叫做回调函数。如果函数没有名称(函数表达式),就叫做匿名回调函数。

回调函数不一定用于异步,同步(阻塞)场景下也会用到回调函数。比如说要求在执行完某些操作后执行回调函数。

举几个栗子:
同步(阻塞)回调:
fn1执行完后执行fn2
在同步场景下,回调函数最后执行
异步回调:
ajax请求
在异步回调中,回调函数有可能不执行,因为时间没有被触发或者条件不满足

回调函数的应用场景:
动态加载js后,图片加载完成后,ajax请求等

另外,最好保证回调存在且必须是函数引用或者函数表达式:
(callback && typeof(callback) === "function") && callback();
6.es6常用新特性:

1.let和const(新的变量声明方式,允许把变量作用域控制在块级里面)
2.解构赋值(对象和数组都适用)
3.promise
一个对象,用来表示并传递异步操作的最终结果
交互方式:将回调函数传入then方法获取最终结果或出错原因
以链式调用代替回调函数层层嵌套
//生成promise实例
var promise = new Promise(function(resolve, reject) {
 //...other code...
 if (/* 异步操作成功 */){
   resolve(value);//resolve函数将Promise对象的状态从Pending变为Resolved
 } else {
   reject(error);//reject函数将Promise对象的状态从Pending变为Rejected
 }
});
//Promise实例生成以后,用then方法分别指定Resolved状态和Reject状态的回调函数。
promise.then(function(value) {
 // success
}, function(err) {
 // failure
});

jQuery.ajax()方法中运用promise:
var http = {
    get: function(url) {
        var promise = new Promise(function(resolve, reject) {
            $.ajax({
                url: url,
                method: 'get',
                success: function(data) {
                    resolve(data);
                },
                error: function(xhr, statusText) {
                    reject(statusText);
                }
            });
        });
        return promise;
    }
};
http.get('data.php').then(function(data) {
    document.write(data);
}, function(err) {
    document.write(err);
});
这里是获取的data值:
/* data.php文件 */
<?php
echo '{"name":"Tom","age":"22"}';

4.箭头函数(简化函数的写法、修复this的指向,箭头函数里this指向方法原来的作用域,即方法绑定在哪个对象上,this就指向哪个对象)
5.多行字符串
6.类(方法名不再需要function关键字)
7.模板对象(${xxx})
8.默认参数(把默认值放在函数声明里)
9.iterable类型(Map):具有iterable类型的集合都可以用for...of来循环遍历
举个栗子:
var m = new Map();
m.set("name", "lysa");//添加新的key-value;
Map对象对应很多的方法,如.set(),.has(),.delete()
10.modules(模块):
举个栗子:
在a.js中,
export var aa="test";
export function bb(){
    do something;
}

在b.js中导入a.js的某个变量并引用:import {aa, bb} from 'a'
console.log(aa);

在b.js中导入a.js整个文件并引用:import * as xxx from 'a'
console.log(xxx.aa);
7.cookie和web storage的区别?
1.与服务器的交互上
cookie始终会在http同源请求头上携带(即使不需要),在浏览器端和客户端之间传来传去
localStorage和sessionStorage不会主动发送给服务器,仅保存在本地
2.储存大小
cookie由于不同浏览器的限制,大小在4KB左右
localStorage和sessionStorage储存大小达5MB或以上
3.过期时间
cookie与设置的有效期时间有关
localStorage存储持久数据,除非主动删除
sessionStorage在浏览器关闭后自动删除
8.cookie和session的区别?
cookie数据存放在客户的浏览器,session存储在服务器上
考虑到安全性
建议:
将登陆等重要信息存放为session
其它信息可以放在cookie中
9.一个页面从输入url到页面加载,这个过程都发生了什么?
简洁版:
1.浏览器根据请求的url向服务器发送请求
2.服务求处理完成后返回数据,浏览器接收文件
3.浏览器对加载到的资源(html, css, js, image等)进行语法解析
4.载入解析到的资源,渲染页面
10.事件委派
优点:减少事件注册;简化DOM更新时,相应事件的更新
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title></title>
        </head>
        <body>
            <ul id="lists"></ul>
        </body>
        <script>
            var oUl=document.getElementById("lists");
            var fragment=document.createDocumentFragment();
            for(i=0;i<10;i++){
                var oLi=document.createElement("li");
                oLi.innerHTML="this is an item";
                oLi.index=i;
                fragment.appendChild(oLi);
            }
            oUl.appendChild(fragment);
    //        oUl.onclick=function(ev){
    //            console.log("ev是:",ev)
    //            var src=ev.target||ev.srcElement;
    //            if(src.tagName==="LI"){
    //                console.log(src.index)
    //            }
    //        }
    
            oUl.addEventListener("click",function(ev){
                var src=ev.target||ev.srcElement;
                if(src.tagName==="LI"){
                    console.log(src.index);
                }
            },false)//默认false,事件句柄在冒泡阶段执行
        </script>
    </html>
11.拖拽

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8" />
            <title></title>
            <style>
                body{margin:0;}
                .box{width:100px;height: 100px;background: red;position: relative;cursor: move;}
            </style>
        </head>
        <body>
            <div class="box"></div>
            <script>
                var oBox=document.getElementsByClassName("box")[0];
                oBox.onmousedown=function(ev){
                    // 鼠标按下的状态
                    var ev=ev||window.event;//兼容ie浏览器
                    // 元素的x坐标和y坐标
                    var l_dis=oBox.offsetLeft;
                    var t_dis=oBox.offsetHeight;
                    
                    // 1.记录下鼠标的x坐标和y坐标
                    // 2.计算鼠标离元素边距的距离
                    var disX=ev.clientX-l_dis;
                    var disY=ev.clientY-t_dis;
                    var that=this;
                    
                    // 鼠标移动的状态
                    document.documentElement.onmousemove=function(ev){
                        var ev=ev||window.event;
                        // 这里的ev.pageX值是否和ev.clientX值一致?
                        // 计算元素现在的位置
                        let new_l_dis=ev.pageX-disX;
                        let new_t_dis=ev.pageY-disY;
                        
                        // 最大可拖动的距离
                        let max_l_dis=window.innerWidth-oBox.offsetWidth;
                        let max_t_dis=window.innerHeight-oBox.offsetHeight;
                        
                        // 边界问题
                        if(new_l_dis<0){
                            new_l_dis=0;
                        }
                        if(new_l_dis>max_l_dis){
                            new_l_dis=max_l_dis;
                        }
                        if(new_t_dis<0){
                            new_t_dis=0;
                        }
                        if(new_t_dis>max_t_dis){
                            new_t_dis=max_t_dis;
                        }
                        oBox.style.left=new_l_dis+"px";
                        that.style.top=new_t_dis+"px";
                        
                        document.documentElement.onmouseup=function(){//鼠标抬起,不再移动
                            this.onmousemove=null;
                            this.onmouseup=null;
                        }
                    }
                }
            </script>
        </body>
    </html>
12.DOM事件
        DOM事件(3个级别):DOM0;DOM2;DOM3;
        DOM0特点:js与html强耦合;绑定的速度快,不需要操作dom,可直接在元素上绑定
        dom.onclick=function(){
            do something;
        }
        
        DOM2特点:可以在一个元素中添加不同的事件,事件不会覆盖;
        dom.addEventListener("click",function(){},false)
        
        DOM3特点:在dom2的基础上,添加更多的事件类型;可自定义事件;
        var event=new Event("test");
        dom.addEventListener("test",function(){},false);
        setTimeout(function(){
            oBox.dispatchEvent(event);
        },2000)


        DOM事件捕获的具体流程:
        window->document->html->body->...->目标元素
        
        事件流:
        捕获->目标阶段->冒泡
        
        一个节点上注册多个事件:
        解决响应优先级:event.stopImmediatePropagation();
13.javascript如何实现一个类,怎么实例化这个类?

1.构造函数法
            // 构造函数法(this + prototype)
            // 用new关键字生成实例 对象
            
            function Person(name, age) {
                this.name = name;
                this.age = age;
            }
            
            Person.prototype.info = function() {
                alert(this.name + this.age);                
            }
            
            var xiaoLi = new Person("李佳", 26);
            xiaoLi.info();

2.es6语法糖
            // es6语法糖
            // 用new关键字生成实例化对象
            
            
            // 优点:避免了使用prototype
            class Person {
                  constructor(x, y) {
                      this.x = x;
                      this.y = y;
                  }
                  info() {
                      alert(this.x + this.y);
                  }
                  
            }
              
            var xiaoLi = new Person("李佳", 26);
            xiaoLi.info();
14.解释作用域链
1.全局函数无法查看局部函数的细节,局部函数可以访问全局函数的属性
2.当前作用域没有找到属性或方法,可以向上层乃至全局寻找,这种形式就是作用域链
15. .call()和.apply()的区别?
.call()和.apply()是每个函数都包含而非继承来的方法,用于改变函数的作用域
两方法作用相同
区别在接收参数的方式不同,call()要求明确传入每一个参数;

扩充作用域,举个栗子:
window.color = "red";
var o = {color: "blue"};

function sayColor() {
    alert(this.color);
}

sayColor(); // red
sayColor.call(this); // red
sayColor.call(window); //red
sayColor.call(o); // blue
15.jsonp的原理
利用script标签可以跨域访问的特性,动态创建script标签,给标签设置src属性。
通过script标签访问跨域的地址,返回一个参数为请求数据的callback的回调函数,而不再直接是json数据。

具体实现步骤:
原生:
    $(document).ready(function(){
        var url = "http://www.practice-zhao.com/student.php?id=1&callback=jsonhandle";
        var obj = $('<script><\/script>');
        obj.attr("src",url);
        $("body").append(obj);
    });
jquery:
    $(document).ready(function(){
        $.ajax({
            type : "get",
            async: false,
            url : "http://www.practice-zhao.com/student.php?id=1",
            dataType: "jsonp",
            jsonp:"callback", //请求php的参数名
            jsonpCallback: "jsonhandle",//要执行的回调函数
            success : function(data) {
                alert("age:" + data.age + "name:" + data.name);
            }

        });
    });
16.jquery链式调用
原理:对象方法上最后加上return this;语句
优点:节省代码,代码看起来更优雅
17.原型和原型链
先记两个干巴巴的结论:
举个栗子:
function Person(name, age, job) {
 this.name = name;
 this.age = age;
 this.job = job;
 this.sayName = function() { alert(this.name) } 
}
var person1 = new Person('Zaxlct', 28, 'Software Engineer');
var person2 = new Person('Mick', 23, 'Doctor');
console.log(person1.constructor == Person); //true
console.log(person2.constructor == Person); //true
1.实例的构造函数属性(constructor)指向构造函数。
var A = new Person();
Person.prototype = A;
2.原型对象(Person.prototype)是 构造函数(Person)的一个实例。

O_o华丽丽
130 声望4 粉丝

纳尼?