Js基础回顾--javascript常用内容

2019/09/13 10:56
阅读数 13

Javascript核心基础内容

01.作用域和作用域链

  作用域决定了变量或函数的可访问性,在 JavaScript 中有两种作用域类型:局部作用域和全局作用域。拥有全局作用域的变量/函数有如下三种:

1.在最外层定义的变量和函数拥有全局作用域
2.未声明直接赋值的变量拥有全局作用域
3.在浏览器中,定义在window对象上的属性和方法,拥有全局作用域
函数作用域指的是函数内部定义的变量只有函数内部可以访问。作用域链指的是在当前作用域中找不到某个变量时,引擎就会在外层嵌套的作用域(父函数)中继续搜索,一直搜索最外层的作用域(也就是全局作用域)为止。这样一条有序的列表,称为作用域链,作用域链的最前端一定是当前作用域。
如下:c()函数需要打印name变量值,c()内部没有name变量,然后在父函数b()中查找name变量,b()中也没有;继续在父函数的父函数a()中找,a()函数有name=‘jack’,最终打印jack。
<script>
function a(){
    name='jack'
    function b(){
        function c(){console.log(name)}
        c();
    }
    b();
}
a();//输出jack
</script>

  在ES6以后有了块级作用域,let和const:在同一个作用域内不能声明两次;let和const不像var一样可以声明提升;let和const在使用前必须先声明。

02 闭包

  我们知道在js中子函数可以有权访问父函数的变量、父函数的父函数的变量、一直到全局变量,如果子函数不被销毁,整条作用域链上的变量仍然保存在内存中不会被释放。开发中如果我们想访问一个函数(父函数)的成员,可以让这个函数返回一个函数(子函数),然后通过这个子函数来获取父函数中的内部成员。一个简单的栗子:

function user () {
    var name = 'jack'
    //方法内部的方法,可通过这个内部方法返回父函数的内部成员    
    return function getName () {
         return name
    }
}
var funcGetName = user()
console.log(funcGetName()) //打印 jack
funcGetName=null//释放整个作用域链的内存

03 变量提升和函数提升

1.变量提升(将变量的声明提升到该变量作用域的最顶端)

function func(){
    console.log(a)//undefined
    var a=1;
    console.log(a)//1
}
func()


//作用类似如下 function func2(){ var a; console.log(a)//undefined a=1; console.log(a)//1 } func2()

2.函数提升(函数式声明的函数会将声明和定义一起提升到作用域的最顶端)

//栗子1.函数式声明,函数提升会将函数的声明和定义一起提升到作用域的最顶端
console.log(func) //打印函数定义
func();           //执行函数

function func(){
    console.log('hello')
}

//栗子2.函数式声明的优先级高于变量声明
console.log(func)//打印函数定义,而不是变量func=1
function func (){ console.log('hello') } var func=1 //3.变量声明的函数不会提升定义 console.log(func)//undefined func();//报错,func is not a function var func=function (){ console.log('hello') }

04 function中的arguments

  arguments本质是一个类似Array的Object对象,内部包含了函数的所有实参。当我们传参个数不确定是可以使用arguments。看一个获取实参最大值的栗子:

function getmax(){
    console.log(arguments.length)//实参个数
    let max=arguments[0];
   for(i=0;i<arguments.length;i++){
        max=max>arguments[i]?max:arguments[i]
    };
    return max;
}
console.log(getmax(10,2,-3,4,5,6))//获取最大值10
console.log(getmax.length)//形参个数

05 原型和原型链

  在理解原型和原型链前,首先要理解普通对象和函数对象(凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。)

var o1 = {}; 
var o2 =new Object();
var o3 = new f1();

function f1(){}; 
var f2 = function(){};
var f3 = new Function('str','console.log(str)');

console.log(typeof f1); //function 
console.log(typeof f2); //function 
console.log(typeof f3); //function   

console.log(typeof o1); //object 
console.log(typeof o2); //object 
console.log(typeof o3); //object

  每个对象都有 _proto_属性原型:用于指向创建它的构造函数的原型对象。person1.__proto__ == Person.prototype),但只有函数对象才有 prototype原型对象在 Person 创建的时候,创建了一个它的实例对象并赋值给Person的 prototype属性,原型对象【Person.prototype】是 构造函数【Person】的一个实例)属性。

  原型链的终点:

Function.prototype//是一个空函数,其他所有的原型都是一个Object对象
//函数原型对象的原型就是Object的原型对象
Function.prototype.__proto__==Object.prototype
//Object的对象原型对象的原型为null
Object.prototype.__proto__//null

  所有构造函数的原型对象的类型都是object(不考虑类的继承),Function除外(原型对象是构造函数的一个实例,Function的实例都是函数,所有Fucntion的原型对象的类型为function)JS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做_proto__的内置属性,用于指向创建它的构造函数的原型对象。person1.__proto__ == Person.prototype

person.__proto__ 是什么?   //Person.prototype
Person.__proto__ 是什么?    //Function.prototype
Person.prototype.__proto__ 是什么? //Object.prototype
Object.__proto__ 是什么?           //Function.prototype
Object.prototype__proto__ 是什么?  //null,新建一个Object的实例时,原型为null

  原型链的形成是真正是靠__proto__ 而非prototype

var Animal = function(){};
var Dog = function(){};

 animal.price = 2000;
 Dog.prototype = animal;
 var tidy = new Dog();
 console.log(dog.price) //undefined  
  dog本身没有price属性,然后去找Dog._proto_=Function.prototype,Fnnction.prototype也没有price属性,
  继续找Function.prototype._proto_=Object.prototype,Object.prototype也没有,
  继续找Object.prototype._proto_=null,找不到报一个undefined
console.log(tidy.price) // 2000
tidy本身没有price属性,然后去找tidy._proto_=Dog.prototype=animal,
  animal有price属性2000,打印2000

06 this指向谁?

一句话说明:this就指向最终调用函数的对象。根据应用的场景,可以把this的指向分为以下几种:

//1.直接调用的函数this指向Window
function foo(){console.log(this)}
foo();//this-->Window

//2.函数作为window内置函数的回调函数调用:this指向window
setTimeout(() => {
    console.log(this)
}, 1000);


//3.通过对象实例调用 var obj={ name:"普通对象", foo:function(){console.log(this)} } obj.foo()//this-->obj //4.函数作为数组的一个元素,通过数组下标调用的:this指向这个数组 let arr=[function(){console.log(this)},1,"hello"]; arr[0]() //this-->arr //5. 函数作为构造函数,用new关键字调用时:this指向新new出的对象 function Person(){ this.name='jack', console.log(this) } let person=new Person()//this-->新创建的对象

07 call、apply、bind的用法和区别

  call、apply、bind都是为了改变某个函数运行时的上下文而存在的(就是为了改变函数中this的指向),其中call/apply会立即执行函数,bind只是改变this的指向而不执行,看一个栗子:

var mycalc = {
    add:function(n1,n2){
        console.log(n1+n2)
        console.log(this);
    }
}
var addfunc = mycalc.add;

//1.直接调用this->Window
addfunc(1,2); //3,指向的是Window

//apply,call,bind的this都指向第一个参数
//2.apply,参数列表是一个数组
addfunc.apply(mycalc,[1,2])//3,mycalc

//3.call,参数列表不定长度
addfunc.call(mycalc,1,2)

//4.bind,参数列表不定长度,返回一个新函数,不会自动执行
var newFunc= addfunc.bind(mycalc,1,2)
newFunc()//3,mycalc

//一些的使用场景
//返回数组最大值,this不用指定第一个参数可以设置为null let arr=[2,1,5,3,4]; Math.max.apply(null,arr); //让arguments类数组有数组的特性 function toArray(){ console.log([].slice.apply(arguments)); } toArray(1,2,3,4,5)//[1,2,3,4,5]

参考文章:

  【1】https://www.jianshu.com/p/dee9f8b14771

  【2】https://segmentfault.com/a/1190000012772040?utm_source=tag-newest

 

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部