尚硅谷 JavaScript 笔记(张晓飞 2019 高级版)

本文深入探讨JavaScript中的数据类型、对象、函数、内存管理和原型链。讲解了如何判断数据类型、对象的创建模式、闭包的工作原理以及内存溢出和泄漏。此外,还讨论了JavaScript的单线程执行、事件循环模型以及Web Workers的使用,帮助读者深入理解JS的运行机制。
摘要由CSDN通过智能技术生成

数据类型

  • 分类

    • 基本(值)类型
      • string: 任意字符串
      • number: 任意数字
      • boolean: true false
      • undefined: undefined
      • null: null
    • 对象(引用)类型
      • object: 任意对象
      • function: 一个特别的对象(可以执行)
      • array: 一种特别的对象(属性有数值下标属性,数组内部的元素是有序的)
  • 判断

    • typeof: 返回数据类型的字符串表达typeof tmp==="undefined",array和object的typeof都是object所以不能区别
    • instanceof: 实例
    • =/ 一般判断全等用===这个不做数据转化a===undefined 一般用于null undefined的判断(因为他们只有一个值)
  • undefined与null区别

    • undefined 表示只有定义没有赋值
    • null 表示定义了赋值了值为null
    • 设置null的初衷是为了表示这个变量准备被赋值成对象,但是目前还没有赋值,所以typeof null的结果是object
    • null的另一个作用是将没有用的对象的地址设置为null,当内存没人指的时候就会释放,被垃圾回收器回收
  • 在什么时候将变量的值赋值为null

    • 在初始化但是临时不赋值的时候,为了标记
    • 在变量不用的时候释放内存
  • 严格区分数据类型与变量类型

    • 数据的类型
      • 基本类型
      • 对象类型
    • 变量类型(实际上指的是变量值的类型,是存了数字还是地址)
      • 基本类型: 保存的是基本类型数据
      • 引用类型: 保存的是地址的路径

数据,变量,与内存

  • 数据: 存储在内存中代表特定信息的东西,本质上是010101..
  • 内存: 内存条通电之后可以存储数据的空间(临时的),一个内存块里面有两个小数据一个是地址值一个是存的值
    • 内存的分类有栈,堆
    • 栈里面存全局变量和局部变量
    • 堆里面是对象的本身(但是表示堆的标识符是在堆空间里面的)
  • 变量: 可变换的量,由变量名和值组成,每个变量名都占用一小块内存

内存中所有的操作的目标都是数据,操作的运算有:

  • 算数运算

  • 逻辑运算

  • 赋值运算

  • 运行函数

  • 数据,变量,内存之间的关系

内存是存储数据的临时空间,变量是内存的标识

  • 内存与赋值的问题

var a=XXX;a中到底存了什么?

  • 当XXX是基本数据类型,那么他保存的就是这个数据
  • 当XXX是对象的时候,保存的是对象的地址值
  • 当XXX是变量的时候,保存的是XXX的内存内容(两者都有可能)
  • 当两个引用变量指向了同一个对象,通过一个变量修改数据,那么另一个变量看到的是修改后的数据
  • 对象在函数传递参数的时候是将内容赋值给了形参,对象赋值的内容是地址,所以相当于是传地址
  • 值得注意这两种情况
  var a={
   age:12};
  var b=a;
  a={
   age:13};
  // 此时ab指向的地址发生了改变,两者不相干
  var fun=function(v){
   
    v={
   age:15};
  }
  fun(b);
  // 此时b并不会发生改变因为v是一个**独立的变量**,最开始是和b指向了一个地址,但是赋值之后指向的地址发生了改变,所以v的变化不会影响b
  • JS是如何管理内存的
    1. 内存生命周期
    • 分配小的内存空间,获得小内存的使用权
    • 存储数据进行反复操作
    • 没人指向小内存空间的时候释放空间
    1. 释放内存
    • 程序或者作用域结束的时候释放作用域里面的变量(注意这里指的是释放标识符,小内存早就被释放了)
    • 对于对象使用垃圾回收器回收

对象

  • 什么是对象
    • 多个数据的封装体
    • 用来保存多个数据的容器
    • 一个对象代表现实中的一个事物
  • 为什么要用对象
    • 统一管理多个元素
  • 对象的组成
    • 属性
    • 方法(特别的属性–属性值是函数)
  • 访问对象内部的元素
    • OBJ.属性名 当属性名是关键字/包含-/包含/变量名不确定,用变量存储变量名的时候不能用
    • OBJ["属性名"]

函数

  • 为什么要用函数
    • 提高代码复用
  • 如何定义函数
    • 函数声明
    • 表达式
  • 函数的调用
    • fun.call(obj): 让一个函数成为一个陌生属性的方法是JS的一大特性
    • AAA()

回调函数

  • 什么是回调函数

    • 自己定义的
    • 自己没有调用(指的是没有写明,例如window.onclick就是一个没有调用的回调函数,是在特定时间自动执行的)
    • 最后执行了
  • 常见的回调函数

    • dom事件回调函数(与用户交互比较重要的点,发生时间的DOM元素)
    • 定时器回调函数
    • ajax回调函数(与后台交互比较重要的点)
    • 生命周期回调函数

IIFE

IIFE(Immediately-Invoked Function Expression) 立即调用函数表达式

一般来说我们写的是非匿名函数,这样可以将他存起来,当时我们也可以写没有对象接受内容的匿名函数,因为没有标识符标识他,所以他必须立即执行,否则以后就无法调用了(这里的非匿名不是说setInterval(function(){},1000)这种,因为传值的时候相当于赋值了)

对于上述的匿名函数我们必须要当场执行

function(){
   
  alert("123")'
}();

在后面加上([参数列表])就可以执行了,因为我们将前面当作了一个整体去执行,最好前面也加上一个()变成

(function(){
   
  alert("123")'
})();

加上()之后函数定义的变量就从之前的全局变量变成了局部变量,好处有:

  • 隐藏实现:这样的话其他函数就看不到这个函数了(因为本身他就要匿名,最好不要让其他函数看见)
  • 不会污染外部的命名空间

于是我们可以像C++中封装函数一样去封装一个JS函数

(function(){
   
  function work1(){
   
    // true work code 1;
  }
  function work2(){
   
  // true work code 2;
  }
  function work2(){
   
  // true work code 3;
  }
  window.XXX=function(){
   
    if()...
      return work1();
    if()...
      return work2();
    return work3();
  }
})();

我们希望实现一个模块,但是不想暴露细节函数,于是全员匿名,最后提供一个中控函数绑定到window,这在之后就是创建模块的方法,例如

var myModule = (function module(){
   
  var someThing = "123";
  var otherThing = [1,2,3];

  function doSomeThing(){
   
    console.log(someThing);
  }

  function doOtherThing(){
   
    console.log(otherThing);
  }

  return {
   
    doSomeThing:doSomeThing,
    doOtherThing:doOtherThing
  }
})();

myModule.doSomeThing();
myModule.doOtherThing();

函数中的this

注意的一点,对于如下的例子

function fun1(){
   
  function fun2(){
   
    console.log(this);
  }
  fun2();
}
fun1();

这里输出的this是window,同时请注意以下代码

var tmp=new Object;
function fun1(){
   
  console.log(this);
  function fun2(){
   
    console.log(this);
  }
  fun2();
}
tmp.fun=fun1;
tmp.fun();

返回

{fun: ƒ}
VM144:5 Window {0: Window, window: Window, self: Window, document: document, name: "", location: Location, …}

相当于只要不是明确是对象调的都是window

  • this是什么
    • 任何函数本质上都是通过对象调用的
    • 所有函数内部都有一个变量this
    • 他指向的是调用函数的当前对象

JS的分号问题

  • 可加可不加
  • 但是如果不加分号导致二义性要加(小中括号开头的),例如
    var a=3
    (function(){
         ...})()
    
    会被认为是要执行一个名字叫做3的函数,参数列表为fun…
    var a=3(function(){
         ...})()
    
    那么就要加了
  • 注意: JS在代码发布的时候会有一个合理压缩的过程,例如:
    var a=123;
    function my_first_function(){
         
      // ohh here is a note
      return 3;
    }
    
    会被压缩成
    var a=123;function b(){
         return 3;}
    
    不仅压行,还直接改函数名…

prototype

任何函数都具有prototype属性,一个函数默认指向了一个Object空对象(原型对象),也就是说在函数被创建的时候会JS会默认创建一个对象,这个对象的内容是空的

原型对象的元素是给事例对象用的,

prototype中还有一个元素是constructor这是一个引用变量,指向了指向prototype的对象,也就是构造函数与原型对象有一个相互引用的关系

显式原型与隐式原型

  • 每个函数function都有一个prototype,也就是显式原型
  • 每个事例对象都有一个__proto__,可以称为隐式原型
  • 事例对象的__proto__的值是构造函数的prototype
  • 注意:显式原型与隐式原型都是引用对象,指向的是原型,验证方法就是在创建元素后修改显式原型的内容
  • 在ES6之前程序员可以直接操作显式原型,但是不能操作隐式原型

与其他语言不同的是,大部分语言采用的是基于类的基础而JS是采用基于对象的继承,这就导致JS存在原型链

原型链

我们可以尝试一直输出一个对象的__porto__

function Fn(){
   
    var FnFunctionVals=1;
        return 0;
    }
    return 666;
}
var fn=new Fn();
console.log("Fn IS ",fn);
console.log("Fn PROTO",fn.__proto__);
console.log("Fn PROTO PROTO",fn.__proto__.__proto__);
console.log("Fn PROTO PROTO PROTO",fn.__proto__.__proto__.__proto__);

console.log("Fn IS ",Fn);
console.log("Fn PROTO",Fn.__proto__);
console.log("Fn PROTO PROTO",Fn.__proto__.__proto__);
console.log("Fn PROTO PROTO PROTO",Fn.__proto__.__proto__.__proto__);

结果

Fn IS  Fn__proto__: Object
Fn PROTO Objectconstructor: ƒ Fn()__proto__: Object
Fn PROTO PROTO Objectconstructor: ƒ Object()hasOwnProperty: ƒ hasOwnProperty()isPrototypeOf: ƒ isPrototypeOf()arguments: (...)caller: (...)length: 1name: "isPrototypeOf"__proto__: ƒ ()[[Scopes]]: Scopes[0]propertyIsEnumerable: ƒ propertyIsEnumerable()toLocaleString: ƒ toLocaleString()toString: ƒ toString()valueOf: ƒ valueOf()__defineGetter__: ƒ __defineGetter__()__defineSetter__: ƒ __defineSetter__()__lookupGetter__: ƒ __lookupGetter__()__lookupSetter__: ƒ __lookupSetter__()get __proto__: ƒ __proto__()set __proto__: ƒ __proto__()
Fn PROTO PROTO PROTO null
Fn IS  ƒ Fn(){
   
        var FnFunctionVals=1;
        function tmp(){
   
         return 666;
    }
Fn PROTO ƒ () {
    [native code] }
Fn PROTO PROTO Objectconstructor: ƒ Object()hasOwnProperty: ƒ hasOwnProperty()isPrototypeOf: ƒ isPrototypeOf()propertyIsEnumerable: ƒ propertyIsEnumerable()toLocaleString: ƒ toLocaleString()toString: ƒ toString()valueOf: ƒ valueOf()__defineGetter__: ƒ __defineGetter__()__defineSetter__: ƒ __defineSetter__()__lookupGetter__: ƒ __lookupGetter__
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Liukairui

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值