JavaScript基础--------三座大山(前端面试必考)

Posted zhaozhaoli

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript基础--------三座大山(前端面试必考)相关的知识,希望对你有一定的参考价值。

1.原型和原型链

2.作用域和闭包

3.异步和单线程

被称为javascript的三座大山

 

原型和原型链:

在JavaScript中,数组,对象和函数被称为引用类型,他们都有一个__proto__属性,该属性是一个对象(我们称之为隐式原型)

技术分享图片

arr数组的构造函数是Array,Array构造函数中有一个prototype属性,(我们暂时称之为显式原型)

技术分享图片

arr是构造函数的实例对象,arr中的__proto__对象指向构造函数中的prototype对象

技术分享图片

接下来是一个简单的demo

 1 //创建一个构造函数
 2         function Animal(name){
 3             this.name = name
 4         }
 5         Animal.prototype.eat = function(){
 6             console.log(‘Animal--eat‘)
 7         }
 8         // 用new初始化一个Animal的实例对象
 9         var dog = new Animal(‘xiaohuang‘)
10         
11         console.log(dog.name)
12         console.log(dog.eat())

输出结果为

技术分享图片

看一下dog对象中有什么属性

技术分享图片

调用dog的属性和方法时,会先从dog本身去查找,如果dog本身没有那个属性或方法,就会去dog的__proto__原型中去查找,而__proto__又指向Animal的prototype(看第二个constructor对象,指向Animal),这就是原型链

再来一个demo

 1 //创建一个构造函数
 2         function Animal(name){
 3             this.name = name
 4         }
 5         //创建一个Hashiqi的构造函数
 6         function Hashiqi(){
 7         }
 8         Animal.prototype.eat = function(){
 9             console.log(‘Animal--eat‘)
10         }
11         Hashiqi.prototype.color = function(){
12             console.log(‘Hashiqi---color‘)
13         }
14         //改变prototype的指向
15         Animal.prototype = new Hashiqi()
16         // 用new初始化一个Animal的实例对象
17         var dog = new Animal(‘xiaohuang‘)
18         
19         console.log(dog.name)
20         console.log(dog.eat())
21         console.log(dog.color())

这个demo中改变了Animal的prototype,将其指向一个Hashiqi的实例对象,我们来看结果。此时dog.eat()会报错,dog.color正常输出

技术分享图片

 

第三个demo,比较上档次一点

 

 1 <div id="wrapper">this is wrapper</div>
 2     <script>
 3         //创建一个Elem的构造函数
 4        function Elem(id){
 5            //获取dom元素
 6            this.id = document.getElementById(id)
 7        }
 8        Elem.prototype.html = function(val){
 9            //如果val为空,则打印dom元素的innerhtml值
10            if(val == null){
11                console.log(this.id.innerHTML)
12                //返回this,可以用来进行链式操作
13                return this
14            }else{
15                this.id.innerHTML = val
16                return this
17            }
18        }
19        //绑定事件
20        Elem.prototype.on = function(type, fn){
21            this.id.addEventListener(type,fn)
22        }
23        
24     </script>

首先new一个实例对象:  var el = new Elem(‘wrapper‘)

            el.html(‘ ookook ‘).on(‘click‘, function(){  console.log(‘this is ook‘) }  )

接下来点击ookook就会打印this is ook

 

 作用域和闭包:

JavaScript中有函数作用域和全局作用域(es6中用let声明的变量具有块作用域)

 函数作用域是在一个函数中有效,全局作用域在全局都有效

*函数内部如果变量和全局变量重名了,则在该函数内部,以函数变量为准

*函数外部无法访问函数内部定义的变量(该变量是函数私有的),不过函数内部可以访问函数外部的全局变量

 

  1.变量提升

javascript中声明并定义一个变量时,会把声明提前,以下会先打印出undefined,再打印出10

 

1      console.log(a)
2         var a = 10
3         console.log(a)

 

相当于

1         var a
2         console.log(a)
3         a = 10
4         console.log(a)    

函数声明也是,以下函数相当于把整个fn提到作用域的最上面,所以调用fn时会正常打印jack

1 fn(‘jack‘)
2         function fn (name){
3             console.log(name)
4         }

不过函数表达式不行,以下是一个函数表达式,JavaScript会把var fn提到作用域最上面,没有吧函数提上去,所以会报错

1 fn("jack");
2       
3       var fn = function(name) {
4         console.log(name);
5       };

 

  2.闭包

  使用场景:1.函数作为参数传递  2.函数作为返回值传递

  三言两语说不清楚,我们来看一个demo

 1 function F1(){
 2         var a = 100
 3         //返回一个函数
 4         return function(){
 5             console.log(a)
 6         }
 7       }
 8 
 9       var a = 200;
10       var f1 = F1(); //将f1指向F1
11       f1()

 

第11行输出结果是100

第11行调用f1的时候要打印a变量,return的函数中没有a变量,所以a是个自由变量,要去声明该函数(不是调用)的父级作用域去查找,即functin F1中,a=100

  闭包在开发中的应用

  以下函数的目的是:当_list中没有val值时,返回true并把val添加到_list中

  这个demo使用了闭包,在外部无法直接访问_list这个Fn函数的私有变量,这样可以保证数据不被污染,提高了安全性

 1 function Fn(){
 2           var _list = []
 3             
 4           return function(val){
 5               if(_list.indexOf(val) < 0){
 6                   _list.push(val)
 7                   return true
 8               }else{
 9                   return false
10               }
11           }
12       }
13 
14       var f1 = Fn()
15       console.log(f1(10))   //true
16       console.log(f1(10))   //false
17       console.log(f1(20))   //true
18       console.log(f1(20))   //false

 

 

异步和单线程

  异步和单线程是相辅相成的,js是一门单线程脚本语言,所以需要异步来辅助

  异步和同步的区别: 异步会阻塞程序的执行,同步不会阻塞程序的执行,

          比如只有在执行完alert后才会打印100,如果你不去点击弹框的确定键,console.log就永远不会执行,这就是同步的阻塞

           第二个demo中,会马上就打印100,两秒后再打印setTimeout,这就是异步不会阻塞程序执行

1     alert(‘ook‘)
2      console.log(100)

 

1 setTimeout(function(){
2             console.log(‘setTimeout‘)
3         },2000)
4         console.log(100)

 

  在哪些场景中会异步执行

  JavaScript中以下三种情况会异步执行  1.定时任务:setTimeout, setInterval  2.网络请求:ajax请求,动态加载<img>图标  3.事件绑定:比如‘click’等

 

  看一个demo

  以下函数的目的是在页面中创建5个a标签,点击第一个a标签打印1,点击第二个a标签打印2,以此类推。可执行结果都是5,为什么呢?

  因为click是一个异步事件,计算机不知道用户什么时候会点击,所以不能够是同步,不然用户不点击,程序就永远无法往下执行。

  异步事件会先拿出来放到一个队列里,等同步事件执行完了,再来执行异步事件

  所以当你点击a标签的时候,i已经循环到5了(i是全局变量。这也涉及到了js作用域:该函数没有定义i变量,就要去声明该函数的父级作用域中去找,而不是调 用)

 1     for(var i = 0; i < 5; i++){
 2             //创建一个a标签
 3             var a = document.createElement(‘a‘)
 4             a.innerHTML = i + 1 + ‘<br>‘
 5             //给a标签绑定click事件
 6             a.addEventListener(‘click‘, function(e){
 7                 e.preventDefault()
 8                 console.log(i)
 9             })
10             //将a标签添加到wrpper中
11             document.querySelector(‘#wrapper‘).appendChild(a)
12         }

 

 

解决这个问题就可以用闭包,用一个function把给a添加时间的地方包起来,(function(i){})(i) 这是函数的自调用。注意:自调用前要加分号,也就是第4行结束要加分号,不然js会分不清何时开始自调用,会报错

 1  for(var i = 0; i < 5; i++){
 2             //创建一个a标签
 3             var a = document.createElement(‘a‘)
 4             a.innerHTML = i+1 + ‘<br>‘;
 5             //给a标签绑定click事件
 6             (function(i){
 7                 a.addEventListener(‘click‘, function(e){
 8                 e.preventDefault()
 9                 console.log(i+1)
10             })
11             })(i)
12             //将a标签添加到wrpper中
13             document.querySelector(‘#wrapper‘).appendChild(a)
14         }

 



以上是关于JavaScript基础--------三座大山(前端面试必考)的主要内容,如果未能解决你的问题,请参考以下文章

前端JavaScript面试技巧视频教程 js面试课程 共8章

JavaScript闭包(必考三座大山之二)

JavaScript异步(必考三座大山之三)——第四集:async-await

理解JS的三座大山

慕课网-前端JavaScrpt基础面试技巧-学习笔记

JavaScript原型和原型链(必考三座大三之一)