javascript 重要概念

本文深入探讨JavaScript中的变量提升特性,包括var、let的区别,作用域规则,以及全局与函数作用域的具体表现。同时,解析了this关键字的多种行为,Promise的工作原理,类的定义与继承方式,以及常见类型判断方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 var


  • 存在变量提升
  • 只有全局作用域与函数作用域
  • 不存在块作用域
  • 允许在相同作用域内,重复声明同一个变量,后者会覆盖前面的变量声明
/**** 变量提升****/
name = "aa"
var name;

//编译时
var name;
name = "aa"
console.log(name) //aa

console.log(a);//undefined 
var a = 3;

 

/**** 只有全局作用域与函数作用域****/

var name = "abc";

function fn (){
    console.log(name)
    var name = "fn-abc";
}

fn()

//编译时

var name = "abc";
function fn (){
    //当函数中用var 定义变量时 提升至fn 函数作用域中
    var name;
    console.log(name) //undefined
    var name = "fn-abc";
}

fn()

/*******
    LHS 查找关系 
    
    var a = "ok" 有var 关键字定义 a (有赋值=的操作)都是LHS 左查找
******
   
/
/**
 * 当JavaScript引擎执行LHS查询时,
 * 如果在顶层作用域中无法找到目标变量,PS:当前顶层作用域是fn 函数
 * 那么,就会在全局作用域查找该变量,如果找到改变量输出,PS:案例函数在全局对象中查找到name1 = 1,所以输出1
 * 如果未找到则抛出undefined
 */
var name = "abc";
var  name1 = 1
function fn (){
    console.log(name1) //1
    console.log(name) //undefined
    var name = "fn-abc";
}

fn()

 

/**不存在块作用域 */
for(var i =0;i<3;i++){
    console.log(i) //1,2,3
}
console.log(i) //3 

//编译时 变量i提前
var i;
for(i =0;i<3;i++){
    console.log(i) //1,2,3
}
console.log(i) //3 

/**允许在相同作用域内,重复声明同一个变量,后者会覆盖前面的变量声明 */

var name = "abc"
var name = "changename"
console.log(name) //changename

let


  •  不存在变量提升,只在定义的本身
  • 有块作用域
  • 不允许在相同作用域内,重复声明同一个变量。
/**不存在变量提升,只在定义的本身 */
console.log(name) //直接抛出异常报错
let name = "123"

/**有块作用域 */
for(let i =0;i<3;i++){
    console.log(i) //1,2,3
}

console.log(i) // i is not defined

/**不允许在相同作用域内,重复声明同一个变量 */
let name = "name"
let name = "changename" //直接跑错,抛出异常

 

Promise 


Promise 是异步编程的一种解决方案

任务状态管理

  • resolve 成功状态,对应 Promise.resolve
  • reject 失败状态,对应 Promise.reject
  • error 异常状态, 对应 Promise.reject 或 new Promise().catch(onRejected)

 

THIS 关键字理解


在浏览器中执行代码时,全局作用域里this和函数中的this 都指向window全局对象。

当函数被new关键字创建对象或被当做对象调用时 this 指向执行对象本身

console.log(this) //指向window

var firstName = "Peter",
lastName = "Ally";
​
function showFullName () {
    //这个函数里的"this"会储存window对象的值
    //因为与变量firstName和lastName一样,showFullName()函数是在全局作用域里定义的
    console.log (this.firstName + " " + this.lastName);
 }

/***
    当函数被new关键字创建对象或被当做对象调用时 this 指向执行对象本身
***/

function fn() {
    console.log(this) //fn
}
new fn()

var Dog = {
    say() {
        console.log(this) //Dog
    }
}
Dog.say();

 

this最容易用错的情况

this关键词在下列情况下最容易被用错:

  • 当使用this的方法被“借用”时;
  • 当使用this的方法被用作回调函数时;
  • this被用于闭包-内部函数里时。
/**
 * 当this 的方法被借用时,this 指向借用对象dog
 */

 var person = {
     name:'小明',
     age:18,
     say(){
         console.log(this.name,this.age) //大黄 1
     }
 }

 var dog = {
     name:'大黄',
     age:1
 }

 //采用apply 将say方法中的this 执行dog 对象
 person.say.apply(dog)
/**
 * 当this 的方法被用作回调函数时
 * 当上下文环境改变时,在一个对象(persopn)里定义的方法(say)却到另外一个对象(dog)里执行(say)时,
 * this关键词不再指代原对象person,而是指代调用该方法的对象(say()函数),
 * 而say()函数本身的this 指向window全局
 */
var person = {
    name: '小明',
    age: 18,
    say() {
        console.log(this) //window
        console.log(this.name, this.age) //undefined
    }
}

var dog = {
    name: '大黄',
    age: 1,
    say(event) {
        event()
        console.log(this.name, this.age) //大黄,1
    }
}


dog.say(person.say)

//解决方案采用apply bind call 解决

dog.say(person.say.bind(person))
/**
 * 当this被用于闭包-内部函数里时。
 * 匿名函数里的this无法访问外部函数的this,所以在非严格模式下其被绑定了window对象上。
 */
var person = {
    name:'小明',
    age:18,
    say(){
        (function(){
            console.log(this) //window
        })()
    }
}

person.say()

Call方法、Apply方法、Bind函数方法、箭头函数方法 


call,apply,bind这三个方法其实都是继承自Function.prototype中的

主要作用:都是用来改变函数的this对象的指向的。 

call 语法:call(this,args,args) 

apply语法:call(this,[args,args]) 

bind语法:bind(this,args,args)  

/**
 *  @augments apply(this,[args,args])
 *  @augments call(this,args,args)
 * @augments bind(this,args,args)
 */
function jone(name,age,work){
    this.name=name;
    this.age=age;
    this.work=work;
    this.say=function(msg,err){
        console.log(err)
        console.log(msg+",我叫"+this.name+",我今年"+this.age+"岁,我是"+this.work)
    }
}
var jack={
    name:"jack",
    age:'24',
    work:"学生"
}

var pet=new jone();

pet.say.apply(jack,["欢迎您","test"])

pet.say.call(jack,"欢迎您","test")

pet.say.bind(jack,"欢迎您","test")()
/**
    bind 官方提供的源码
**/
if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== 'function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

    var aArgs   = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP    = function() {},
        fBound  = function() {
          // this instanceof fNOP === true时,说明返回的fBound被当做new的构造函数调用
          return fToBind.apply(this instanceof fNOP
                 ? this
                 : oThis,
                 // 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的
                 aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    // 维护原型关系
    if (this.prototype) {
      // Function.prototype doesn't have a prototype property
      fNOP.prototype = this.prototype; 
    }
    // 下行的代码使fBound.prototype是fNOP的实例,因此
    // 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
    fBound.prototype = new fNOP();

    return fBound;
  };
}

Javascript类的定义与继承 


 类的创建:

  • 字面量静态对象;无需使用new 关键字就可直接使用
  • function 构造函数; 使用new 关键字实例化对象
/**
 * 字面量创建对象,静态对象无需实例化,直接使用
    采用构造函数必须使用new 关键字实例化
 */
var persopn = {
    name:"人类",
    age:16
}

console.log(persopn.name)
/**
 * 构造函数采用new关键字实例化对象
 */
var persopns = function(){

}

persopns.prototype = {
    name:"外星人",
    age:100
}

console.log(new persopns().name)
/**
 * 原型链继承
 */
var persopn = function(){
    this.name = "人类"
    this.age = 18
    this.say = function(){
        console.log(this.name,this.age)
    }
}


/**
 * 构造函数采用new关键字实例化对象
 */
var persopns = function(){
    this.name ="外星人"
}
persopns.prototype =  new persopn()

 

Object

Object.create

Object.defineProperty

javascript 类型判断 


以下是存在的类型: 

 

 typeof 可判断:

有如下可知:typeof 只能判断基本类型,对于引用类型输出都是object 无法判断

 

  • Boolean
  • Number
  • String
  • Function
  • Array
  • Date
  • RegExp
  • Object
  • Error
console.log(typeof true) //boolean
console.log(typeof 1) //number
console.log(typeof "a") //string
console.log(typeof function(){}) //function
console.log(typeof []) //object
console.log(typeof new Date()) //object
console.log(typeof /\s/) //object
console.log(typeof {}) //object
console.log(typeof new Error()) //object

Object.prototype.toString.call 方法可判断引用类型:

console.log(Object.prototype.toString.call([])) //[object Array]
console.log(Object.prototype.toString.call(new Date())) //[object Date]
console.log(Object.prototype.toString.call(/\s/))//[object RegExp]
console.log(Object.prototype.toString.call({})) //[object Object]
console.log(Object.prototype.toString.call(new Error())) //[object Error]

instanceof  可判断 A是否为B的实例:

console.log([] instanceof  Array) //true

 constructor 使用constructor属性返回是否是创建此对象函数的引用。

console.log([].constructor==Array) //true

封装一个统一返回类型的方法

/**
 * 
 * @param {*} obj 需判断类型的对象 如:[],"aaa",123,{}等。
 */
let getType = (obj)=>{
    return Object.prototype.toString.call(obj).match(/object\s*(\w*)/)[1]
}

console.log(getType([])) //Array

 javascript 函数的柯里化


柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果。
因此柯里化的过程是逐步传参,逐步缩小函数的适用范围,逐步求解的过程。

/**
 * 
 * @param {*} a 
 * @param {*} b 
 * 函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,
 * 并进行处理
 */
function A(a) {
    return function(b){
        console.log(a+b)
    }
}

A(1)(2)

javascript 原型链  prototype 与 __proto__


prototype 与 __proto__  的作用,让实例都拥有原型对象的属性与方法。原型对象共享属性与方法

 

不同之处:

  • 每个对象都有__proto__属性来标识自己所继承的原型
  • 只有函数才有prototype属性
//每个对象都有__proto__属性来标识自己所继承的原型
var obj = {}

console.log(obj.__proto__) //{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
console.log(obj.prototype) //undefined

//只有函数才有prototype属性
function dog (){

}

console.log(dog.__proto__) //Function 原型对象
console.log(dog.prototype) //{constructor: ƒ dog(), __proto__:Object}

prototype 与 __proto__ 深入理解

prototype

当你创建函数时,JS会为这个函数自动添加prototype属性,值是空对象 值是一个有 constructor 属性的对象,不是空对象

function dog (){

}

/**
 * 当你创建函数时,JS会为这个函数自动添加prototype属性,值是空对象 值是一个有 constructor 属性的对象,不是空对象。
 * prototype 指向原型对象(既创建函数的本身)dog 函数作为new dog()实例的原型对象 
 */
dog.prototype.name = "大黄" //在dog 对象下添加公用属性name 并且赋值

console.log(new dog().__proto__ == dog.prototype) //true

而一旦你把这个函数当作构造函数(constructor)调用(即通过new关键字调用),那么JS就会帮你创建该构造函数的实例,实例继承构造函数prototype的所有属性和方法(实例通过设置自己的__proto__指向承构造函数的prototype来实现这种继承)

/**
 * 当你创建函数时 dog,
 * JS会为这个函数dog自动添加prototype属性,
 * prototype属性中有个constructor 属性的对象,通常称作构造函数
 * {constructor:function dog()}
 */
function dog (){

}

/**
 * 那么JS就会帮你创建该构造函数的实例,function dog()
 * 实例继承构造函数prototype的所有属性和方法
 * 实例通过设置自己的__proto__指向承构造函数的prototype来实现这种继承
 */
console.log(new dog())

console.log(new dog().__proto__ == dog.prototype) //true

详情:  

__proto__

__proto__ 是一个实例指针,指向它的原型对象 

function dog (){

}

dog.__proto__  //dog 函数实例 指向它的原型对象Function
console.log(dog.__proto__ ==Function.prototype) //true

详情: 

FUNCTION.LENGTH


//调用时想拿到参数集合 使用argumnet

function add(a,b,c){

console.log(arguments)//[1,2,3]

}

//表示拿到定义函数时的参数总的数量,但并不能拿到参数具体值

console.log(add.length) //3

add(1,2,3)

JAVASCRIPT 闭包 


 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值