天天看点

尚硅谷JS高级笔记JS高级1、数据类型2、内存3、函数4、this5、分号问题6、原型 prototype7、变量提升与函数提升8、执行上文9、作用域和作用域链10、循环遍历加监听11、闭包12、对象创建模式13、原型链继承

目录

  • JS高级
  • 1、数据类型
    • 1.1、分类
    • 1.2、判断
    • 1.3、类型对象和实例对象的区别
    • 1.4、`undefined` 和 `null` 的区别
  • 2、内存
    • 2.1、数据
    • 2.2、什么是内存
    • 2.3、练习
  • 3、函数
    • 3.1、函数调用
    • 3.2、回调函数
    • 3.3、IIFE
  • 4、this
  • 5、分号问题
  • 6、原型 `prototype`
    • 6.1、什么是原型对象
    • 6.2、显式原型属性和隐式原型属性
    • 6.3、原型链
    • 6.4、构造函数/原型/实体对象的关系
    • 6.5、结论
    • 6.6、原型链属性问题
    • 6.7.instanceof
    • 6.8、面试题
  • 7、变量提升与函数提升
  • 8、执行上文
    • 8.1、代码分类
    • 8.2、 全局执行上下文
    • 8.3、函数执行上下文
    • 8.4、执行上下文栈
    • 8.5、面试题
  • 9、作用域和作用域链
    • 9.1、分类
    • 9.2、作用
    • 9.3、作用域与执行上下文的区别
    • 9.4、面试题
  • 10、循环遍历加监听
  • 11、闭包
    • 11.1、如何产生闭包
    • 11.2、常见闭包
    • 11.3、作用
    • 11.4、生命周期
    • 11.4、应用
    • 11.5、闭包的缺点
    • 11.6、面试题
  • 12、对象创建模式
  • 13、原型链继承

JS高级

1、数据类型

1.1、分类

  • 基本类型
    • String

    • Number

    • boolean

    • undefined

    • null

  • 对象类型
    • Object

    • Function

    • Array

1.2、判断

  • typeof

    • null

      ->

      object

    • Array

      ->

      object

    • 其余都可以判断
  • instanceof

    判断对象的具体类型
  • ===

  • ==

var a;
console.log(a, typeof(a)); //undefined, 'undefined'
a = 'aitguigu';
console.log(typeof(a));// 'string' 
a = null 
console.log(typeof(a));// 'objcet'
           

1.3、类型对象和实例对象的区别

function Person(name, age) { // 构造函数 类型对象
  this.name = name;
  this.age = age;
}
var p = new Person(); // 根据类型创见的实例对象
           

1.4、

undefined

null

的区别

  • undefined

    定义未赋值
  • null

    赋值并定义且值为

    null

2、内存

2.1、数据

  • 特点:可传递、可运算

2.2、什么是内存

  • 内存中存储两种数据:地址值、内部存储的数据
  • 内存分类
    • 堆:对象
    • 栈:全局变量、局部变量、函数名
    尚硅谷JS高级笔记JS高级1、数据类型2、内存3、函数4、this5、分号问题6、原型 prototype7、变量提升与函数提升8、执行上文9、作用域和作用域链10、循环遍历加监听11、闭包12、对象创建模式13、原型链继承

2.3、练习

var obj1 = {name: 'Tom'};
var obj2 = obj1;
obj2.age = 12;
console.log(obj1.age);// 12
function fn(obj) {
    obj.name = 'A';
}
fn(obj1);
console.log(obj2.name); // 'A'

var a = {age: 12};
var b = a;
a = {name: 'BOB', age: 13};
b.age = 14;
console.log(b.age, a.name, a.age);// 14, 'BOB', 13;
function fn2(obj) {
    obj = {age: 15};
}
fn2(a);
console.log(a.age); // 13
           

3、函数

3.1、函数调用

  • 直接调用
  • 通过对象调用
  • new 调用
  • call() 和 apply() 可以让任意函数成为指定对象的方法
var obj = {};
function test() {
    this.name = 'atguigu';
}
test.call(obj);
console.log(obj.name);// atguigu
           

3.2、回调函数

  • 定义且没有调用并最终执行的函数
  • DOM

    事件回调函数
  • 定时器回调函数
  • AJAX

    请求回调函数
  • 生命周期回调函数

3.3、IIFE

  • 隐藏实现
  • 不会污染外部命名空间
  • 可以编写JS模块
(function() {// 匿名函数自调用,向外暴露一个全局函数
  var a = 1;
  function test() {
    console.log(a);
  }
  window.$ = function() {
    return {
        test: test    
    }
 } 
})();
$().test();
           

4、this

  • 任何函数本质上都是通过某个对象来调用
  • 所有函数内部都有一个变量

    this

    ,值是调用函数的对象,如果没有指定,就是

    window

  • test()

    window

  • p.test()

    p

  • new test()

    新创建的对象
  • p.call(obj)

    obj

function Person() {
  getThis = function() {
    console.log(this);
  }
}
var p = new Person();
p.getThis(); // this 是p
var b = p.getThis;
b(); // this 是 window
           

5、分号问题

  • ()

    前要加分号
  • []

    前要加分号
  • JS文件头部没有加分号,合并会导致问题

6、原型

prototype

6.1、什么是原型对象

  • 每个函数都有一个

    prototype

    属性,它默认指向一个

    Object

    空对象,即原型对象
    • prototype

      中有一个属性

      constructor

      ,指向函数对象
    • 空对象是指的没有自定义属性
尚硅谷JS高级笔记JS高级1、数据类型2、内存3、函数4、this5、分号问题6、原型 prototype7、变量提升与函数提升8、执行上文9、作用域和作用域链10、循环遍历加监听11、闭包12、对象创建模式13、原型链继承
console.log(Date.prototype, typeof Date.prototype)// Object
function fun(){}
fun.prototype.test = function(){}
console.log(fun.prototype) // Object
console.log(Date.prototype.constructor == Date) // true
           

6.2、显式原型属性和隐式原型属性

  • 每个函数都有一个显式原型属性

    prototype

  • 每个实例对象都有一个隐式原型属性

    proto

  • 对象的隐式原型的值为其对应构造函数的显式原型的值
function Fn() { // 内部语句 Fn.prototype = {}
}
var fn = new Fn();// 内部语句 fn.__proto___ = Fn.prototype
console.log(fn.__proto__===Fn.prototype); // true
           
尚硅谷JS高级笔记JS高级1、数据类型2、内存3、函数4、this5、分号问题6、原型 prototype7、变量提升与函数提升8、执行上文9、作用域和作用域链10、循环遍历加监听11、闭包12、对象创建模式13、原型链继承

6.3、原型链

  • 别名:隐式原型链
  • 访问一个属性时
    • 先从自身属性中查找,找到并返回
    • 如果没有再沿着

      __proto__

      这条链向上查找,找到并返回
    • 如果最终没找到,返回

      undefined

尚硅谷JS高级笔记JS高级1、数据类型2、内存3、函数4、this5、分号问题6、原型 prototype7、变量提升与函数提升8、执行上文9、作用域和作用域链10、循环遍历加监听11、闭包12、对象创建模式13、原型链继承

6.4、构造函数/原型/实体对象的关系

尚硅谷JS高级笔记JS高级1、数据类型2、内存3、函数4、this5、分号问题6、原型 prototype7、变量提升与函数提升8、执行上文9、作用域和作用域链10、循环遍历加监听11、闭包12、对象创建模式13、原型链继承
  • Foo

    是一个函数对象,因此拥有

    prototype

    属性,默认指向

    Foo

    函数对象的原型对象
  • Foo

    同时也是一个实例对象,

    var Foo = new Function();

    因此拥有

    __proto__

    属性,指向

    Function

    函数对象的原型对象
  • Function

    是一个函数对象,因此拥有

    prototype

    属性与

    Foo

    __proto__

    属性都指向

    Function

    函数对象的原型对象
  • Function

    同时也是一个实例对象,

    var Function = new Function();

    因此拥有

    __proto__

    属性,指向

    Function

    函数的原型对象
  • Object

    是一个函数对象,因此拥有

    prototype

    属性,默认指向

    Object

    函数对象的原型对象
  • Object

    同时也是一个实例对象,

    var Object = new Function();

    因此拥有

    __proto__

    属性,指向

    Function

    函数对象的原型对象
  • Function

    的原型对象是

    Object

    的原型对象的实例

6.5、结论

  • 结论1:函数的显式原型指向的对象默认是空

    Object

    实例对象(

    Object

    除外)
  • 结论2:所有函数都是

    Function

    的实例
  • 结论3:

    Object

    的原型对象是原型链的尽头
// 验证
console.log(Object.prototype instanceof Object) //false
console.log(Function.prototype instanceof Object) // true
console.log(Function.prototype === Function.__proto__) //true
console.log(Object.prototype.__proto__) // null
           

6.6、原型链属性问题

  • 读取对象属性时会自动在原型链中查找
  • 设置对象属性值时,不会自动查找原型链,如果当前对象中没有此属性,直接添加
  • 方法一般定义在原型中,属性一般通过构造函数定义在对象本身上
// 读取对象属性时会自动在原型链中查找
function Fn() {}
Fn.prototype.a = 'xxx'
var fn = new Fn();
console.log(fn.a) // xxx

// 设置对象属性值时,不会自动查找原型链,如果当前对象中没有此属性,直接添加
var fn2 = new Fn()
fn2.a = 'yyy'
console.log(fn2.a, fn.a) // yyy, xxx

// 方法一般定义在原型中,属性一般通过构造函数定义在对象本身
function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.setAge = function (age) {
    this.age = age; // this这个值仍然是指向它原本属于的对象,而不是从原型链上找到它时,它所属于的对象。
}
var person = new Person('xs', 21);
person.setAge(22);
console.log(person);
var person2 = new Person('ws', 31);
person2.setAge(33);
console.log(person2);
console.log(person2.__proto__ === person.__proto__);// true
           

6.7.instanceof

  • 表达式

    A instanceof B

    • A

      实例对象
    • B

      构造函数
  • 如果

    B

    函数的显式原型对象在

    A

    对象的原型链上,返回

    true

    ,否则返回

    false

function Foo(){}
var f1 = new Foo()
console.log(f1 instanceof Foo)
console.log(f1 instanceof Object)
console.log(Object instanceof Function) // true
console.log(Object instanceof Object); // true
console.log(Function instanceof Function) // true
console.log(Function instanceof Object) // true
console.log(Object instanceof Foo) // false
           

6.8、面试题

// 1
function A () {}
A.prototype.n = 1
var b = new A()
A.prototype = {
    n: 2,
    m: 3
}
var c = new A()
console.log(b.n, b.m, c.n, c.m) // 1, undefined, 2, 3

// 2
var F = function F() {}
Object.prototype.a = function() {
  console.log('a')
}
Function.prototype.b = function() {
  console.log('b')
}
var f = new F()
f.a() // a 
f.b() // 报错
F.a() // a
F.b() // b
           

7、变量提升与函数提升

  • 通过

    var

    定义(声明)的变量,在定义语句之前就可以访问的到,值为

    undefined

  • 通过

    function

    声明的函数,在之前就可以调用,值为函数定义(对象)
var a = 3
function fn() {
  console.log(a)
  var a = 4;
}
fn() // undefined
           

8、执行上文

8.1、代码分类

  • 全局代码
  • 函数代码(局部代码)

8.2、 全局执行上下文

  • 在执行全局代码前,将

    window

    确定为全局执行上下文
  • 对全局数据进行预处理
    • var

      定义的全局变量赋值为

      undefined

      ,添加为

      window

      属性
    • function

      声明的全局函数赋值并添加为

      window

      方法
    • this

      赋值为

      window

  • 开始执行全局代码

8.3、函数执行上下文

  • 在调用函数时,准备执行函数体之前,创建对应的函数执行上下文对象
  • 对局部数据进行预处理
    • 形参变量进行实参赋值添加为执行上下文的属性
    • arguments

      实参列表赋值添加为执行上下文的属性
    • var

      定义的局部变量赋值为

      undefined

      添加为上下文属性
    • function

      声明的函数赋值并添加为上下文属性
    • this

      赋值为调用函数对象
  • 开始执行函数体代码

8.4、执行上下文栈

  • 在全局代码执行前,JS引擎会创建一个栈来存储管理所有的执行上下文对象
  • 在全局执行上下文

    window

    确定后,将其添加到栈中(压栈)
  • 在函数执行上下文创建后,将其添加到栈中(压栈)
  • 在当前函数执行完成后,将栈顶的对象移除(出栈)
  • 当所有代码执行完后,栈中只剩下

    window

尚硅谷JS高级笔记JS高级1、数据类型2、内存3、函数4、this5、分号问题6、原型 prototype7、变量提升与函数提升8、执行上文9、作用域和作用域链10、循环遍历加监听11、闭包12、对象创建模式13、原型链继承
var a = 10
var bar = function (x) {
    var b = 5
    foo(x + b)
}
var foo = function (y) {
    var c = 5
    console.log(a + c + y)
}
bar(10)
           

8.5、面试题

// 输出结果是什么 一共几个执行上下文对象 5个
console.log("global begin: " + i) // undefined
var i = 1
foo(1)
function foo(i) {
    if (i == 4)  return
    console.log("foo begin: " + i) // 1 2 3
    foo(i + 1)
    console.log("foo end: " + i); // 3 2 1
}
console.log("global end: " + i); // 1

// 2  
// 先执行变量提升,再执行函数提升
function a() {} 
var a; 
console.log(typeof a); // function

// 3
if (!(b in window)) {
    var b = 1;
}
console.log(b) // undefined

// 4 
var c = 1
function c(c) {
  console.log(c) 
} 
c(2) // c is not a function
           

9、作用域和作用域链

  • 静态的(相对于上下文对象),编写代码时候已经确定

9.1、分类

  • 全局作用域
  • 函数作用域
  • 没有块作用域(ES6有了)

9.2、作用

  • 隔离变量,不同作用域下同名变量不会有冲突

9.3、作用域与执行上下文的区别

  • 创建
    • 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定,而不是在函数调用时
    • 全局上下文是在全局作用域确定后,JS代码马上执行前创建
    • 函数上下文是在调用函数时,函数体代码执行前创建
  • 性质
    • 作用域是静态的,只要函数定义好就不会变化
    • 执行上下文是动态的,调用函数时创建,函数结束后销毁
  • 联系
    • 执行上下文(对象)从属于所在作用域
    • 全局上下文对应全局作用域,函数上下文对应函数作用域

9.4、面试题

// 1 
var x = 10;
function fn() {
    console.log(x);
}
function show(f) {
    var x = 20;
    f()
}
show(fn) // 10
// 2
var fn = function () {
    console.log(fn)
}
fn() //函数

//3
var obj = {
    fn2 : function () {
        console.log(fn2)
    }
}
obj.fn2() // Uncaught ReferenceError: fn2 is not defined
           

10、循环遍历加监听

var btns = document.getElementsByTagName('button');
// 写法1 错误
for(var i = 0, length = btns.length; i < length; i++) {
  var btn = btns[i]
   btn.onclick = function() {
      alert(i + 1)
   }
}
// i 是全局作用域
console.log(i)
// 写法2 
for(var i = 0, length = btns.length; i < length; i++) {
  var btn = btns[i]
  btn.index = i 
  btn.onclick = function() {
      alert(this.index + 1)
  }
}
console.log(i)
// 写法3 利用了闭包
for(var i = 0, length = btns.length; i < length; i++) {
  (function(i) {
    var btn = btns[i]
    btn.onclick = function() {
        alert(i + 1)
    }
  })(i)
}
console.log(i)
           
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
           

11、闭包

11.1、如何产生闭包

  • 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包
  • 闭包是嵌套的内部函数
  • 包含被引用变量(函数)的对象
  • 注意:闭包存在于嵌套的内部函数
  • 产生闭包的条件
    • 函数嵌套
    • 内部函数引用了外部函数的数据(变量/函数)
  • 注意:
    • 创建闭包是再调用外部函数时创建
    • 参数和变量不会被垃圾回收机制回收
      • 垃圾回收机制,函数没有被引用执行完以后这个函数的作用域就会被销毁,如果一个函数被其他变量引用,这个函数的作用域将不会被销毁(简单来说就是函数里面的变量会被保存下来,可以理解成全局变量)
function fn1() {
    var a = 2
    function fn2() {
        console.log(a);
    }
}
fn1()
           

11.2、常见闭包

  • 将函数作为另外一个函数的返回值
  • 将函数作为实参传递给另一个函数
function fn1() {
    var a = 2
    function fn2() {
        a++;
        console.log(a);
    }
    return fn2
}
var f = fn1();
f()  // 3
f() // 4

function showDelay(msg, time) {
    setTimeout(function () {
        alert(msg)
    }, time)
}
showDelay(1, 1000)

function fn1() {
    var a = 2
    function fn2() {
        a++;
        console.log(a);
    }
    function fn3() {
        a--;
        console.log(a);
    }
    return fn3
}
var f = fn1();
f()  // 1
f() // 0
           

11.3、作用

  • 延长了局部变量的生命周期,使用函数内部的变量在函数执行完后,仍存活在内存中
  • 让函数外部可以操作(读写)到函数内部的数据(变量/函数)

11.4、生命周期

  • 产生:在嵌套内部函数定义执行时就产生了,不是在调用时
  • 销毁:在嵌套的内部函数成为垃圾对象时
function fn1() {
    var a = 2
    function fn2() {
        a++;
        console.log(a);
    }
    return fn2
}
var f = fn1();
f = null
           

11.4、应用

  • 定义JS模块
    • 具有特定功能的JS文件
    • 将所有的数据和功能都封装在一个函数中
    • 只向外暴露一个包含n个方法的对象或函数
    • 模块的使用者,只需要通过模块暴露的对象调用方法来实现对应功能
function myModel() {
    var msg = 'aaa'
    function doSomething() {
        console.log('doSomething()' + msg.toUpperCase())
    }
    function doOtherthing() {
        console.log('doOtherthing()')
    }
    return {
        doSomething : doSomething,
        doOtherthing : doOtherthing
    }
}
           
<script type="text/javascript" src="modelDemo.js"></script>
<script>
    var fn = myModel();
    console.log(fn.doSomething()); // doSomething() AAA
    console.log(fn.doOtherthing()); // undefined
</script>
           
  • 方法2
(function (w) {
    var msg = 'aaa'
    function doSomething() {
        console.log('doSomething()' + msg.toUpperCase())
    }
    function doOtherthing() {
        console.log('doOtherthing()')
    }
    w.myModel = {
        doSomething : doSomething,
        doOtherthing : doOtherthing
    }
})(window)
           
<script type="text/javascript" src="modelDemo2.js"></script>
<script>
    var fn = window.myModel;
    console.log(fn.doSomething());
    console.log(fn.doOtherthing());
</script>
           

11.5、闭包的缺点

  • 函数执行完后,函数的局部变量没有释放,占用内存时间会边长
  • 容易造成内存泄露
    • 意外的全局变量
// 1
function fn() {
  a = 3
  console.log(a)
}
fn()

// 2
var a = setInterval(function() {
  console.log('----')
}, 1000)
// clearInterval(a)

// 闭包
           
  • 解决:
    • 尽量避免闭包
    • 及时释放

11.6、面试题

// 面试题 1
var name =  'The Window'
var object = {
    name: 'myObject',
    getNameFunc: function () {
        return function () {
            return this.name
        }
    }
}
alert(object.getNameFunc()()); // The Window

// 面试题 2
var name2 = 'The Window'
var object2 = {
    name2: 'myObject',
    getNameFunc: function() {
      var that = this; 
      return function() {
        return that.name2; // 闭包
      }
    }
}
alert(object2.getNameFunc()())// myObject 

// 面试题 3 
function fun(n, o) {
    console.log(o);
    return {
        fun: function (m) {
            return fun(m,n)
        }
    }
}
var a = fun(0);// undefined
a.fun(1); // 0
a.fun(2); // 0
a.fun(3); // 0
var b = fun(0).fun(1).fun(2).fun(3);//undefined,1,2,3
var c = fun(0).fun(1);// undefined, 1
c.fun(2); // 1
c.fun(3); // 1 
           

12、对象创建模式

  • 构造函数式创建
    • 场景:初始时不确定对象内部数据
    • 缺点:语句太多
  • 字面量式创建
    • 场景:初始时对象内部数据确定
    • 缺点:如果创建多个对象,有重复代码
  • 工厂模式创建
    • 场景:需要创建多个对象
    • 缺点:对象没有一个具体的类型,都是Object类型
  • 自定义构造函数模式
    • 场景:需要创建多个类型确定的对象
    • 缺点:数据相同,浪费内存
  • 构造函数 + 原型 的组合模式
    • 场景:属性在函数中初始化,方法添加到原型上
    • 缺点:需要创建多个类型确定的对象
// 1 
var obj = new Object();
obj.name = 'Joke';
obj.age = '18';
// 2
var obj2 = {name: 'Tom', age: 12}
function createPerson(name, age) {
  var obj = {
    name: name,
    age: age,
  }
  return obj;
}
// 3
var obj3 = createPerson('HHH', 22);
var obj4 = createPerson('GGG', 23);
// 4
function Person(name, age) {
    this.name = name;
    this.age = age;
}
// 5 
Person.prototype.setName = function (name) {
    this.name = name;
}
var person = new Person('AAA', 45);
console.log(obj);
console.log(obj2);
console.log(obj3);
console.log(obj4);
person.setName('张三');
console.log(person)
           

13、原型链继承

  • 子类型的原型为父类型的实例
  • 借用构造函数
    • 在子类型构造函数中通用

      call()

      调用父类型构造函数
  • 原型链 + 借用构造函数的组合继承
    • 利用原型链实现对父类型对象的方法继承
    • 利用

      super()

      借用父类型构建函数初始化相同属性
// 方式一
// 父类型
function Supper() {
    this.supProp = 'supper'
}
Supper.prototype.showSupperProp = function () {
    console.log("s >>> " + this.supProp);
}
// 子类型
function Sub() {
    this.subProp = 'sub'
}
// Sub.prototype = new Supper()
Sub.prototype.showSubProp = function () {
    console.log(this.subProp);
}
var sub = new Sub();
sub.showSupperProp() // Uncaught TypeError: sub.showSupperProp is not a function
           
尚硅谷JS高级笔记JS高级1、数据类型2、内存3、函数4、this5、分号问题6、原型 prototype7、变量提升与函数提升8、执行上文9、作用域和作用域链10、循环遍历加监听11、闭包12、对象创建模式13、原型链继承
// 方式二 
function Person(name, age) {
    this.name = name;
    this.age = age;
}
function Student(name, age, price) {
    Person.call(this, name, age)
    this.price = price;
}
var student = new Student('zs', 23, 299); // Student {name: 'zs', age: 23, price: 299}
           
// 方法三
function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.setName = function (name) {
    this.name = name
}
function Student(name, age, price) {
    Person.call(this, name, age)
    this.price = price;
}
Student.prototype = new Person();
Student.constructor = Student;
Student.prototype.setPrice = function (price) {
    this.price = price;
}
var student = new Student('zs', 29, 30);
student.setName('sss');
console.log(student); // Student {name: 'sss', age: 29, price: 30}
           

继续阅读