JS 站在巨人的肩上


一、JS简介

JavaScript 是一种解释型的脚本语言,被大量地应用于网页中,用以实现网页和浏览者的动态交互。目前几乎所有的浏览器都可以很好地支持 JavaScript。 JavaScript 可以及时响应浏览者的操作,控制页面的行为表现,提高用户体验
JavaScript的组成部分
标准化后的 JavaScript 包含了 3 个组成部分,如图所示。

图 1:JavaScript 组成部分

  1. ECMAScript
    脚本语言的核心内容,它规定了js的语法标准,现在每种浏览器都有对ECMAScript标准的实现。
  2. DOM(Document Object Model)
    文档对象模型,它是HTML和XML文档的应用程序编程接口。浏览器中的DOM把整个网页规划成由节点层级构成的树状结构的文档。用DOM API可以轻松地删除、添加和替换文档树结构中的节点。如addEventListener(),document.getElementById,e.stopPropagation()
  3. BOM(Browser Object Model)
    浏览器对象模型,描述了对浏览器窗口进行访问和操作的方法和接口。如location对象,navigator对象,screen对象,document对象,核心window

二、作用域与作用域链

函数作用域和全局作用域
所谓函数作用域就是说:-》变量在声明它们的函数体内是有定义的

变量提升

var scope="global";
function t(){
    console.log(scope);
    var scope="local"
    console.log(scope);
}
t();
变量提升后相当于
var scope="global";
function t(){
    var scope;
    console.log(scope);
    scope="local"
    console.log(scope);
}
t();
// undefind  local

无块级作用域

function t(flag){
    if(flag){
        var s="ifscope";
        for(var i=0;i<2;i++) 
            ;
    }
    console.log(i);
    console.log(s);
}
t(true);
// 2 ifscope

提示:1.es6之后Let拥有了块级作用域
提示:2.Js中没有用var声明的变量都是全局变量,而且是顶层对象的属性。(window | global)

作用域链

如果在当前函数作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,查找过程形成的链路就叫做作用域链。

name="baikui";
function t(){
    var name="zhangsan";
    function s(){
        var name="lisi";
        console.log(name);//s=>t=>global
    }
    function ss(){
        console.log(name);//ss=>t=>global
    }
    s(); 
    ss();
}
t();
// lisi zhangsan

易错案例

<html>
<head>
    <script type="text/javascript">
        function buttonInit() {
            for (var i = 1; i < 4; i++) {
                var b = document.getElementById("button" + i);
                b.addEventListener("click", function () {
                    console.log("Button" + i);
                }, false);
            }
        }
    </script>
</head>
<body onload="buttonInit()">
    <button id="button1">Button1</button>
    <button id="button2">Button2</button>
    <button id="button3">Button3</button>
</body>
</html>

三、this关键字详解

this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象

JS里面,函数的几种调用方式:

1.普通函数调用

2.作为方法来调用

3.作为构造函数来调用

4.利用apply/call/bind方法来调用

5.es6箭头函数

但是不管函数是按哪种方法来调用的,记住一点:谁调用这个函数或方法,this关键字就指向谁。

1、普通函数调用

var name="xl";
function person(){
  console.log(this.name);
}
person(); //输出 xl
//实际上person是作为全局对象window的一个方法来进行调用的,即window.person();
// 同时window还拥有了另外一个属性name,值为xl

2、作为方法来调用

var name="XL";
var person={
      name:"xl",
      showName:function(){
         console.log(this.name);
      }
    }
person.showName();  //输出  xl
//这里是person对象调用showName方法,很显然this关键字是指向person对象的,所以会输出name
    
var showNameA=person.showName;
showNameA();    //输出  XL
//这里将person.showName方法赋给showNameA变量,此时showNameA变量相当于window对象的一个属性,因此showNameA()执行的时候相当于window.showNameA(),
// 即window对象调用showNameA这个方法,所以this关键字指向window

3、作为构造函数来调用

function  Person(name){
    this.name=name;
 }
 var personB=new Person("xl");
 console.log(personB.name);// 输出 xl
    //这部分代码的解释见下

4、使用apply/call方法来调用
在JS里函数也是对象,因此函数也有方法。从Function.prototype上继承到Function.prototype.call/Function.prototype.apply方法
call/apply方法最大的作用就是能改变this关键字的指向.

Obj.method.apply(AnotherObj,arguments);

var name="XL";
var person={
      name:"xl",
      showName:function(){
         console.log(this.name);
      }
    }
var anotherPerson={name:'xxl'}
person.showName.call(anotherPerson); //输出 "xxl"
person.showName.call(); //输出 "XL"
//这里call方法里面的第一个参数为空,默认指向window。
//虽然showName方法定义在person对象里面,但是使用call方法后,将showName方法里面的this指向了anotherPerson|window。
// 因此最后不会输出xl;
    

5、作为箭头函数来调用

function Timer() {
    this.seconds = 0;
    setInterval( () => this.seconds ++, 1000);//箭头函数内的this指向词法环境,指向实例对象
} 
    
var timer = new Timer();
    
setTimeout( () => console.log(timer.seconds), 3100);
    // 3
   

在构造函数内部的setInterval()内的回调函数(箭头函数),this始终指向实例化的对象,并获取实例化对象的seconds的属性,每1s这个属性的值都会增加1


四、原型和原型链

原型

new 的过程分为三步

var p = new Person('张三',20);
1. var p={}; 初始化一个对象p。
2. p._proto_=Person.prototype;,将对象p的 __proto__ 属性设置为 Person.prototype
3. Person.call(p,”张三”,20);调用构造函数Person来初始化p。

“prototype”和“proto”进行简单的介绍:
对于所有的对象,都有__proto__属性,这个属性对应对象的原型.
对于函数对象,除了__proto__属性之外,还有prototype属性,当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是设置实例的__proto__属性)

原型链

属性查找
function Person(name, age){ 
    this.name = name; 
    this.age = age; 
  } 
Person.prototype.MaxNumber = 9999;
var personObj = new Person("xiaoming", 28); 
console.log(personObj.MaxNumber); // 9999 
console.log(personObj.MinNumber);

在这里插入图片描述 personObj.constructor == personObj.proto.constructor ==function Person

对象创建方式影响原型链

Object.prototype.age=20
var personObj = { 
   name: "张三", 
   getInfo: function(){ 
     console.log(this.name + " is " + this.age + " years old"); 
   }
 } 
console.log(personObj.getInfo());

当使用这种方式创建一个对象的时候,原型链就变成下图了. July对象的原型是”Object.prototype”也就是说对象的构建方式会影响原型链的形式。

在这里插入图片描述


五、同步和异步

单线程

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事,JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM;他的缺点是很多时候CPU是空闲状态,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。

于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)

1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

4)主线程不断重复上面的第三步,因为每个任务都是由一个事件触发的,因此也叫作事件循环。

所谓"回调函数"(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.

function fun1() {
  console.log(1);
}
function fun2() {
  console.log(2);
}
function fun3() {
  console.log(3);
}
fun1();
setTimeout(function(){
  fun2();
},0);//即使是0也会落入任务队列,等到主线程执行结束,才会执行
fun3();
 
// 输出
1
3
2

异步编程

在这里插入图片描述
图片资源还未请求完毕

上图可以看到,我要购买一个东西,当我点进物品的详情页之后,图片资源还未请求完毕,而此时我就可以点击add to cart, 发起另一个请求,js任务列表中的添加购物车事件就会开始执行,它的执行也不会干扰到图片资源的请求任务,这也是异步执行机制的妙处

<html>
<body>
    加入购物车:
    <img src="./hb.png" onload="loadImage()"></img>
    <div onclick="addToCart()">Add to cart</div>
</body>
<script>
    function addToCart() { //回调函数
        alert('已加入购物车')
    }
    function loadImage() {
        alert('图片加载完毕')
    }
</script>
<html>

异步事件:
1、定时器 setTimeout和setInterval
2、事件绑定
3、AJAX
4、promise等


六、promise

ECMAscript 6 原生提供了 Promise 对象。
Promise 对象代表了未来将要发生的事件,用来传递异步操作的消息。
1、有三种状态:

  • pending: 初始状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。

2、包含then 方法
一个Promise必须提供一个then方法来获取其值或原因。
Promise的then方法接受两个参数:promise.then(onFulfilled, onRejected)
then 返回一个promise,可以进行.then链式操作,异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数
3、立即执行

promise 实例创建

要想创建一个 promise 对象、可以使用 new 来调用 Promise 的构造器来进行实例化。

var rf = require("fs");//node 核心模块

let myRequest = new Promise(function (resolve, reject) {
    rf.readFile("./text.txt", 'utf-8', function (err, data) {
        if (err) {
            reject(err);
        } else {
            resolve(data)
        }
    });

})
myRequest.then((value) => {
    console.log('成功结果' + value)
    return value  // then中返回的结果会传递到下个then参数中
}, (reason) => {
    console.log('失败原因' + reason)
})

ES6 class类——语法糖

定义: class (类)作为对象的模板被引入,可以通过 class 关键字定义类。它的本质是函数(function),可以看作一个语法糖,让对象原型的写法更加简单明了、更接近与面向对象的编程思想(类似python、java等)。

function Mold(a,b){
         this.a=a;
         this.b=b;
     }
     Mold.prototype.count=function(){
       return this.a+this.b;
     };
     let sum=new Mold(1,2); 
    console.log(sum.count()) //3
//上下一致
class Mold{
       constructor(a,b){
         this.a=a; //关键字指向着实例对象
         this.b=b;
       }
       count(){
         return this.a+this.b;
       }
       //static fn1(){
       		//console.log("静态方法")
       //}
    }
    let sum=new Mold(1,2);
    console.log(sum.count())  //3

在属性或方法前面使用 static定义类的静态属性和方法;
所有的静态属性和静态方法都不能通过实例化的对象调用;
需要通过类来调用,静态属性和静态方法是类的专属属性和方法,和实例化对象无关,比如数组和数学方法中的:Array.from();Math.random()。

手动封装Promise

const RESLOVED = "RESLOVED"
const REJECTED = "REJECTED"
const PENDING = "PENDING"
class Promise1 {
    constructor(excutor) {
        this.status = PENDING;
        this.value = "";
        this.reason = "";
        this.resloveList = [];
        this.rejectList = []
        this.success = (value) => {
            if (this.status == PENDING) {
                this.value = value;
                this.status = RESLOVED;
                this.resloveList.forEach((fn) => {
                    fn()
                })
            }
        };
        this.filled = (value) => {
            if (this.status == PENDING) {
                this.reason = value
                this.status = REJECTED;
                this.rejectList.forEach((fn) => {
                    fn()
                })
            }
        };
        try {
            excutor(this.success, this.filled)
        } catch (err) {
            this.filled(err)
        }

    }
    then(onFulfilld, onRejected) {
        if (this.status == RESLOVED) {
            onFulfilld(this.value)
        } else if (this.status == REJECTED) {
            onRejected(this.reason)
        } else {  // pending
            // 异步返回采用 发布订阅模式
            this.resloveList.push(() => {
                onFulfilld(this.value)
            })
            this.rejectList.push(() => {
                onRejected(this.reason)
            })
        }
    }
}
module.exports = Promise1 // commmonJs node语法

提示:Promise的其他方法 all、race、resolve 、reject等


总结

以上就是今天要讲的一些JS比较重要的概念和原理、有利于之后学习vue和react框架以及他们底层的框架源码设计模式;之后无论是在浏览器还是在 Node.js 中,都会成为一名更成熟的 JavaScript 开发者
之后大家有相关的学习可以一起再深入探讨

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值