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


Javascript核心基础内容

01.作用域和作用域链

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

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

  在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;iarguments\[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

本文转自 https://www.cnblogs.com/wyy1234/p/11443129.html#_label0_4,如有侵权,请联系删除。