JavaScript三大重点同步异步与作用域和闭包及原型和原型链详解

目录
  • 1. 同步、异步
  • 2. 作用域、闭包
    • 闭包
    • 作用域
  • 3. 原型、原型链
    • 原型(prototype)
    • 原型链

如图所示,JS的三座大山:

  • 同步、异步
  • 作用域、闭包
  • 原型、原型链

1. 同步、异步

JavaScript执行机制,重点有两点:

  • JavaScript是一门单线程语言
  • Event Loop(事件循环)是JavaScript的执行机制

JS为什么是单线程

最初设计JS是用来在浏览器验证表单操控DOM元素的是一门脚本语言,如果js是多线程的,那么两个线程同时对一个DOM元素进行了相互冲突的操作,那么浏览器的解析器是无法执行的。

js为什么需要异步

如果js中不存在异步,只能自上而下执行,如果上一行解析时间很长,那么下面的代码就会被阻塞。

对于用户而言,阻塞就以为着“卡死”,这样就导致了很差的用户体验。比如在进行ajax请求的时候如果没有返回数据后面的代码就没办法执行

JS的事件循环(eventloop)是怎么运作的

  • 首先判断JS是同步还是异步,同步就进入主线程运行,异步就进入event table.
  • 异步任务在event table中注册事件,当满足触发条件后,(触发条件可能是延时也可能是ajax回调),被推入event queue
  • 同步任务进入主线程后一直执行,直到主线程空闲时,才会去event queue中查看是否有可执行的异步任务,如果有就推入主线程中。

如图所示:

那怎么知道主线程执行栈为空呢?js引擎存在monitoring process进程,会持续不断的检查 主线程执行栈是否为空,一旦为空,就会去event queue那里检查是否有等待被调用的函数

宏任务 包含整个script代码块,setTimeout, setIntval

微任务 Promise , process.nextTick

在划分宏任务、微任务的时候并没有提到async/ await的本质就是Promise

setTimeout(function() {
    console.log('4')
})
new Promise(function(resolve) {
    console.log('1') // 同步任务
    resolve()
}).then(function() {
    console.log('3')
})
console.log('2')

执行结果: 1-2-3-4
1. 这段代码作为宏任务,进入主线程。
2. 先遇到setTimeout,那么将其回调函数注册后分发到宏任务event queue.
3. 接下来遇到Promise, new Promise立即执行,then函数分发到微任务event queue
4. 遇到console.log(), 立即执行
5. 整体代码script作为第一个宏任务执行结束, 查看当前有没有可执行的微任务,执行then的回调。(第一轮事件循环结束了,我们开始第二轮循环)
6. 从宏任务的event queue开始,我们发现了宏任务event queue中setTimeout对应的回调函数,立即执行。

console.log('1')
setTimeout(function() {
    console.log('2')
    process.nextTick(function() {
        console.log('3')
    })
    new Promise(function(resolve) {
        console.log('4')
        resolve()
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6')
})
new Promise(function(resolve) {
    console.log('7')
    resolve()
}).then(function() {
    console.log('8')
})
setTimeout(function() {
    console.log('9')
    process.nextTick(function() {
        console.log('10')
    })
    new Promise(function(resolve) {
        console.log('11')
        resolve()
    }).then(function() {
        console.log('12')
    })
})

1.整体script作为第一个宏任务进入主线程,遇到console.log(1)输出1

遇到setTimeout, 其回调函数被分发到宏任务event queue中。我们暂且记为setTimeout1
3.遇到process.nextTick(),其回调函数被分发到微任务event queue中,我们记为process1
4.遇到Promise, new Promise直接执行,输出7.then被分发到微任务event queue中,我们记为then1
又遇到setTimeout,其回调函数被分发到宏任务event queue中,我们记为setTimeout2.
现在开始执行微任务, 我们发现了process1和then1两个微任务,执行process1,输出6,执行then1,输出8, 第一轮事件循环正式结束, 这一轮的结果输出1,7,6,8.那么第二轮事件循环从setTimeout1宏任务开始
5. 首先输出2, 接下来遇到了process.nextTick(),统一被分发到微任务event queue,记为process2
8new Promise立即执行,输出4,then也被分发到微任务event queue中,记为then2
6. 现在开始执行微任务,我们发现有process2和then2两个微任务可以执行输出3,5. 第二轮事件循环结束,第二轮输出2,4,3,5. 第三轮事件循环从setTimeout2哄任务开始
10。 直接输出9,跟第二轮事件循环类似,输出9,11,10,12
7. 完整输出是1,7,6,8,2,4,3,5,9,11,10,12(请注意,node环境下的事件监听依赖libuv与前端环境不完全相同,输出顺序可能会有误差)

async/await用来干什么

用来优化promise的回调问题,被称为是异步的终极解决方案

async/await内部做了什么

async函数会返回一个Promise对象,如果在函数中return一个直接量(普通变量),async会把这个直接量通过Promise.resolve()封装成Promise对象。如果你返回了promise那就以你返回的promise为准。await是在等待,等待运行的结果也就是返回值。await后面通常是一个异步操作(promise),但是这不代表await后面只能跟异步才做,await后面实际是可以接普通函数调用或者直接量。

async相当于 new Promise,await相当于then

await的等待机制

如果await后面跟的不是一个promise,那await后面表达式的运算结果就是它等到的东西,如果await后面跟的是一个promise对象,await它会’阻塞’后面的diamante,等着promise对象resolve,

然后得到resolve的值作为await表达式的运算结果。但是此"阻塞"非彼“阻塞”,这就是await必须用在async函数中的原因。 async函数调用不会造成"阻塞",它内部所有的“阻塞”都被封装在一个promise对象中异步执行(这里的阻塞理解成异步等待更合理)

async/await在使用过程中有什么规定

每个async方法都返回一个promise, await只能出现在async函数中

async/await在什么场景使用

单一的promise链并不能发现async/await的有事,但是如果需要处理由多个promise组成的then链的时候,优势就能体现出来了(Promise通过then链来解决多层回调的问题,现在又用async/awai来进一步优化它)

2. 作用域、闭包

闭包

  • 闭包是指有权访问另外一个函数作用域中的变量的函数(红宝书)
  • 闭包是指那些能够访问自由变量的函数。(MDN)其中自由变量, 指在函数中使用的,但既不是函数参数arguments也不是函数的局部变量的变量,其实就是另外一个函数作用域中的变量。)

作用域

说起闭包,就必须要说说作用域,ES5种只存在两种作用域:

  • 函数作用域。
  • 全局作用域 当访问一个变量时,解释器会首先在当前作用域查找标示符,如果没有找到, 就去父作用域找, 直到找到该变量的标示符或者不在父作用域中
  • 这就是作用域链,每一个子函数都会拷贝上级的作用域, 形成一个作用域的链条。
let a = 1;
function f1() {
    var a = 2
  function f2() {
       var a = 3;
       console.log(a); //3
   }
}

在这段代码中,

  • f1的作用域指向有全局作用域(window) 和它本身
  • 而f2的作用域指向全局作用域(window)、 f1和它本身
  • 而且作用域是从最底层向上找, 直到找到全局作用域window为止
  • 如果全局还没有的话就会报错。闭包产生的本质就是
  • 当前环境中存在指向父级作用域的引用
function f2() {
    var a = 2
    function f3() {
        console.log(a); //2
    }
    return f3;
}
var x = f2();
x();

这里x会拿到父级作用域中的变量, 输出2。

因为在当前环境中,含有对f3的引用, f3恰恰引用了window、 f3和f3的作用域。

因此f3可以访问到f2的作用域的变量。那是不是只有返回函数才算是产生了闭包呢?回到闭包的本质,只需要让父级作用域的引用存在即可。

var f4;
function f5() {
    var a = 2
    f4 = function () {
        console.log(a);
    }
}
f5();
f4();

让f5执行,给f4赋值后,等于说现在f4拥有了window、f5和f4本身这几个作用域的访问权,还是自底向上查找,最近是在f5中找到了a,因此输出2。在这里是外面的变量f4存在着父级作用域的引用,

因此产生了闭包,形式变了,本质没有改变。

场景

  • 返回一个函数
  • 作为函数参数传递
  • 在定时器、 事件监听、 Ajax请求、 跨窗口通信、 Web Workers或者任何异步中,只要使用了回调函数, 实际上就是在使用闭包。

IIFE(立即执行函数表达式) 创建闭包, 保存了全局作用域window和当前函数的作用域。

	var b = 1;
	function foo() {
	    var b = 2;

	    function baz() {
	        console.log(b);
	    }
	    bar(baz);
	}
	function bar(fn) {
	    // 这就是闭包
	    fn();
	}
	// 输出2,而不是1
	foo();
	// 以下的闭包保存的仅仅是window和当前作用域。
	// 定时器
	setTimeout(function timeHandler() {
	   console.log('111');
	}, 100)

	// 事件监听
	// document.body.click(function () {
	//     console.log('DOM Listener');
	// })
	// 立即执行函数
	var c = 2;
	(function IIFE() {
	    // 输出2
	    console.log(c);
	})();

经典的一道题

for (var i = 1; i <= 5; i++) {
    setTimeout(function timer() {
        console.log(i)
    }, 0)
}  // 6 6 6 6 6 6
// 为什么会全部输出6? 如何改进, 让它输出1, 2, 3, 4, 5?

解析:

  • 因为setTimeout为宏任务, 由于JS中单线程eventLoop机制, 在主线程同步任务执行完后才去执行宏任务。
  • 因此循环结束后setTimeout中的回调才依次执行, 但输出i的时候当前作用域没有。

往上一级再找,发现了i,此时循环已经结束,i变成了6,因此会全部输出6。

利用IIFE(立即执行函数表达式)当每次for循环时,把此时的i变量传递到定时器中

	for (var i = 0; i < 5; i++) {
	    (function (j) {
	        setTimeout(() => {
	            console.log(j)
	        }, 1000);
	    })(i)
	}

给定时器传入第三个参数, 作为timer函数的第一个函数参数

for (var i = 0; i < 5; i++) {
	    setTimeout(function (j) {
	        console.log(j)
	    }, 1000, i);
	}

使用ES6中的let

  • let使JS发生革命性的变化, 让JS有函数作用域变为了块级作用域
  • 用let后作用域链不复存在。 代码的作用域以块级为单位,
for (let i = 1; i <= 5; i++) {
    setTimeout(function timer() {
        console.log(i)
    }, 2000)
}

3. 原型、原型链

原型(prototype)

JS中所有函数都会有prototype属性,只有函数才有

其所有的属性和方法都能被构造函数的实例对象共享访问

代码如下:

function Person(name){
		this.name = name
	}
	Person.prototype.sayHello(){
		console.log('sayHello')
	}
	let p1 = new Person();
	let p2 = new Person();
	console.log(p1.sayHello) //sayHello
	console.log(p2.sayHello) //sayHello

构造函数(constructor)

JS中constructor存在每个函数的prototype属性中,其保存了指向该函数的引用

Person.prototype.constructor ==Person   //true

原型链(_ _ proto _ _)

JS中对象都会有个内置属性,即__proto__,(隐式原型链的属性),一般情况下执行创建它的构造函数的prototype的属性,另外函数比较特殊,也会有该属性

p1.__proto__ == Person.prototype

JS 引擎查找摸个属性时,先查找对象本身是否存在该属性,如果不存在就会在原型链上一层一层进行查找

有几个面试经常会问的几个问题

如何精确地判断短数组的类型

[] instanceof Array   //[].__proto__ == Array.prototype
Object.prototype.toString.call([])  //[Object Array]
Array.isArray([]) //true
[].constructor ==Array

下面代码输出什么

Object instanceof Function //true
Function instanceof Object // true

实现一个原型链继承

	function Person(name){
		this.name = name
	}
	Person.prototype.sayHello(){
		console.log('sayHello')
	}
	function Boy(){};
	Boy.prototype = new Person();
	let b1 = new Boy();
	b1.sayHello() //sayHello

原型、原型链、构造函数、实例的关系

1.instanceof检测构造函数与实例的关系:

	function Person () {.........}
	person = new Person ()
	res = person instanceof Person
	res  // true

2.实例继承原型上的定义的属性:

	function Person () {........}
	Person.prototype.type = 'object n'
	person = new Person ()
	res = person.type
	res  // object n

3.实例访问 ===> 原型

实例通过__proto__访问到原型 person.proto=== Person.prototype

4.原型访问 ===> 构造函数

原型通过constructor属性访问构造函数 Person.prototype.constructor === Person

5.实例访问===>构造函数

person.proto.constructor === Person

原型链

在读取一个实例的属性的过程中,如果属性在该实例中没有找到,那么就会循着 proto 指定的原型上去寻找,如果还找不到,则寻找原型的原型:

实例上寻找

function Person() {}
    Person.prototype.type = "object name Person";
    person = new Person();
    person.type = "我是实例的自有属性";
    res = Reflect.ownKeys(person); //尝试获取到自有属性
    console.log(res);
    res = person.type;
    console.log(res); //我是实例的自有属性(通过原型链向上搜索优先搜索实例里的)

原型上寻找

function Person() {}
    Person.prototype.type = "object name Person";
    person = new Person();
    res = Reflect.ownKeys(person); //尝试获取到自有属性
    console.log(res);
    res = person.type;
    console.log(res); //object name Person

原型的原型上寻找

function Person() {}
    Person.prototype.type = "object name Person";
    function Child() {}
    Child.prototype = new Person();
    p = new Child();
    res = [p instanceof Object, p instanceof Person, p instanceof Child];
    console.log(res); //[true, true, true] p同时属于Object,Person, Child
    res = p.type; //层层搜索
    console.log(res); //object name Person (原型链上搜索)
    console.dir(Person);
    console.dir(Child);

原型链上搜索

原型同样也可以通过 proto 访问到原型的原型,比方说这里有个构造函数 Child 然后“继承”前者的有一个构造函数 Person,然后 new Child 得到实例 p;

当访问 p 中的一个非自有属性的时候,就会通过 proto 作为桥梁连接起来的一系列原型、原型的原型、原型的原型的原型直到 Object 构造函数为止;

原型链搜索搜到 null 为止,搜不到那访问的这个属性就停止:

function Person() {}
  Person.prototype.type = "object name Person";
  function Child() {}
  Child.prototype = new Person();
  p = new Child();
  res = p.__proto__;
  console.log(res);         //Person {}
  res = p.__proto__.__proto__;
  console.log(res);         //Person {type:'object name Person'}
  res = p.__proto__.__proto__.__proto__;
  console.log(res);         //{.....}
  res = p.__proto__.__proto__.__proto__.__proto__;
  console.log(res);         //null

继承

  • JS 中一切皆对象(所有的数据类型都可以用对象来表示),必须有一种机制,把所有的对象联系起来,实现类似的“继承”机制。
  • 不同于大部分面向对象语言,ES6 之前并没有引入类(class)的概念,JS 并非通过类而是通过构造函数来创建实例,javascript中的继承是通过原型链来体现的。
  • 其基本思想是利用原型让一个引用类型继承另一个引用继承的属性和方法。

什么是继承

  • js中,继承是一种允许我们在已有类的基础上创建新类的机制;它可以使用现有类的所有功能,并在无需重新编写
  • 原来的类的情况下对这些功能进行扩展。

为什么要有继承

提高代码的重用性、较少代码的冗余

目前我总结的一共有6种继承方式

  • 原型链继承
  • 借用构造函数继承
  • 组合式继承(原型链+构造函数)
  • 原型式继承
  • 寄生式继承
  • 寄生组合式继承
function Person(name){
	this.name = name;
	this.sum=function(){
		alert('this.name',this.name)
	}
}
Person.prototype.age = 100

原型链继承

实现方式: 利用原型链的特点继承,让实例的原型等于父类的实例

优点: 实例可以继承父类的构造个函数,实例的构造函数,父类的原型

缺点: 不能向父类传递参数,由于实例的原型等于父类的实例,那么改变父类的属性,实例的属性也会跟着改变

function child(){
	this.name="xiaoming"
}
child.prototype = new Person()
let child1 = new Child()
child1.name //xiaoming
child1.age //100
child1 instanceof Person //true

借用构造函数继承

实现方式: 使用call/apply将父类的构造函数引入子类函数

优点: 可以祢补原型链继承的缺点,可以向父类传递参数,只继承父类构造函数的属性

缺点: 不能复用,每次使用需要重新调用,每个实例都是父类构造函数的副本,比较臃肿

	function child(){
		Person.call(this,'xiaoming')
	}
	let child1 = new child()
	child1.name //xiaoming
	child1.age //100
	child1 instanceof Person //false

组合式继承

实现方式: 复用+可传递参数

优点: 基于原型链的优点和借用构造函数的优点

缺点: 调用两遍父类函数

	function child(){
		Person.call(this,'xiaoming')
	}
	child.prototype = new Person
	let child1 = new child()
	child1.name //xiaoming
	child1.age //100
	child1 instanceof Person //true
	child instanceof Person //false

原型式继承

实现方式: 函数包装对象,返回对象的引用,这个函数就变成可以随时添加实例或者对象,Object.create()就是这个原理

优点: 复用一个对象用函数包装

缺点: 所有实例都继承在原型上面 无法复用

	function child(obj){
		 function F(){}
		 F.prototype = obj
		 return new F()
	}
	let child1 = new Person()
	let child2 = child(child1)
	child2.age //100

寄生式继承

实现方式: 在原型式继承外面包了一个壳子

优点: 创建一个新对象

缺点: 没有用到实例 无法复用

	function child(obj){
		 function F(){}
		 F.prototype = obj
		 return new F()
	}
	let child1 = new Person()
	function subObject(){
		let sub =child(child1)
		sub.name='xiaoming'
		return sub
	}
	let child2 = subObject(child1)
	typeof subObject //function
	typeof child2 //object
	child2.age //100

寄生组合式继承

实现方式: 在函数内返回对象的调用

优点: 函数的实例等于另外的一个实例,使用call/apply引入另一个构造函数,可传递参数,修复了组合继承的问题

缺点: 无法复用

	function child(obj){
			 function F(){}
			 F.prototype = obj
			 return new F()
		}
		let child1 = child(Person.prototype)
		function Sub(){
			Person.call(this)
		}
		Sub.prototype = child
		child.constructor = Sub
		let sub1 = new Sub()
		sub1.age //100

到此这篇关于JavaScript三大重点同步异步与作用域和闭包及原型和原型链详解的文章就介绍到这了,更多相关JavaScript 同步异步内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 一文详解JavaScript闭包典型应用

    目录 1.应用 1.1 模拟私有变量 1.2 柯里化 1.3 偏函数 1.4 防抖 1.5 节流 2.性能问题 2.1 内存泄漏 2.2 常见的内存泄漏 3.闭包与循环体 3.1 这段代码输出啥 3.2 改造方法 4.总结 1.应用 以下这几个方面是我们开发中最为常用到的,同时也是面试中回答比较稳的几个方面. 1.1 模拟私有变量 我们都知道JS是基于对象的语言,JS强调的是对象,而非类的概念,在ES6中,可以通过class关键字模拟类,生成对象实例. 通过class模拟出来的类,仍然无法实现传

  • Javascript的作用域、作用域链以及闭包详解

    一.javascript中的作用域 ①全局变量-函数体外部进行声明 ②局部变量-函数体内部进行声明 1)函数级作用域 javascript语言中局部变量不同于C#.Java等高级语言,在这些高级语言内部,采用的块级作用域中会声明新的变量,这些变量不会影响到外部作用域. 而javascript则采用的是函数级作用域,也就是说js创建作用域的单位是函数. 例如: 在C#当中我们写如下代码: static void Main(string[] args) { for (var x = 1; x < 1

  • 关于javascript解决闭包漏洞的一个问题详解

    目录 解决闭包漏洞的一个问题 问题原理: 方法一: 方法二: 解决办法: 解决方法二: 总结 解决闭包漏洞的一个问题 在不修改下面代码的情况下,修改obj的内容 var o = (()=>{ var obj = { a:1, b:2, }; return { get :(n)=>{ return obj[n] } } })() 上面代码就是一个典型的闭包模式.屏蔽掉obj本身.只能访问闭包返回的数据而不能去修改数据源本身,但是他的数据源是一个对象,这就会出现一个漏洞!!!!,而上面的代码就会出

  • 一文剖析JavaScript中闭包的难点

    目录 一.作用域基本介绍 1. 全局作用域 2. 函数作用域 3. 块级作用域 二.什么是闭包 1. 闭包的基本概念 2. 闭包产生的原因 3. 闭包的表现形式 三.如何解决循环输出问题 1. 利用 IIFE 2. 使用 ES6 中的 let 3. 定时器传入第三个参数 一.作用域基本介绍 ES6之前只有全局作用域与函数作用域两种,ES6出现之后,新增了块级作用域. 1. 全局作用域 在JavaScript中,全局变量是挂载在window对象下的变量,所以在网页中的任何位置你都可以使用并且访问到

  • JavaScript闭包原理与使用介绍

    目录 1. 认识闭包 2. 变量的作用域和生命周期 2.1 变量的作用域 2.2 变量的生命周期 3. 闭包的概念及其作用 3.1 闭包的概念 3.2 闭包的应用 3.2.1 保存私有变量 3.2.2 使用闭包实现节流 1. 认识闭包 闭包有一个很经典的场景:使用 for循环给上面5个按钮绑定点击事件. <button type="button" class='button'>按钮</button> <button type="button&qu

  • JavaScript 中的作用域与闭包

    目录 一.JavaScript 是一门编译语言 1.1 传统编译语言的编译步骤 1.2 JavaScript 与传统编译语言的区别 二.作用域(Scope) 2.1 LHS查询 和 RHS查询 2.2 作用域嵌套 2.3 ReferenceError 和 TypeError (1)ReferenceError (2)TypeError (3)ReferenceError 和 TypeError 的区别 小结 三.词法作用域 3.1 词法阶段 3.2 词法作用域 查找规则 3.3 欺骗词法 ——

  • JavaScript闭包中难点深入分析

    目录 初识闭包 什么是闭包 如何产生闭包 产生闭包条件 闭包的作用 闭包的生命周期 闭包的应用 闭包的缺点及解决方法 闭包案例 初识闭包 闭包可谓是JS的一大难点也是面试中常见的问题之一,今天开始梳理一下闭包的知识,请诸君品鉴. 什么是闭包 闭包是嵌套的内部函数:内部函数包含被引用变量(函数)的对象.闭包存在于嵌套的内部函数中,例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“.在本质上,闭包是将函数内部和函数外部连接起来.当然如何

  • JavaScript三大重点同步异步与作用域和闭包及原型和原型链详解

    目录 1. 同步.异步 2. 作用域.闭包 闭包 作用域 3. 原型.原型链 原型(prototype) 原型链 如图所示,JS的三座大山: 同步.异步 作用域.闭包 原型.原型链 1. 同步.异步 JavaScript执行机制,重点有两点: JavaScript是一门单线程语言 Event Loop(事件循环)是JavaScript的执行机制 JS为什么是单线程 最初设计JS是用来在浏览器验证表单操控DOM元素的是一门脚本语言,如果js是多线程的,那么两个线程同时对一个DOM元素进行了相互冲突

  • JS难点同步异步和作用域与闭包及原型和原型链详解

    目录 JS三座大山 同步异步 同步异步区别 作用域.闭包 函数作用域链 块作用域 闭包 闭包解决用var导致下标错误的问题 投票机 闭包两个面试题 原型.原型链 原型对象 原型链 完整原型链图 JS三座大山 同步异步 前端中只有两个操作是异步的: 定时器异步执行; ajax异步请求 编译器解析+执行代码原理: 1.编译器从上往下逐一解析代码 2.判断代码是同步还是异步 同步:立即执行 异步:不执行.放入事件队列池 3.等所有同步执行完毕开始执行异步 同步异步区别 api : 异步有回调,同步没有

  • JavaScript作用域、闭包、对象与原型链概念及用法实例总结

    本文实例讲述了JavaScript作用域.闭包.对象与原型链概念及用法.分享给大家供大家参考,具体如下: 1 JavaScript变量作用域 1.1 函数作用域 没有块作用域:即作用域不是以{}包围的,其作用域完成由函数来决定,因而if /for等语句中的花括号不是独立的作用域. 如前述,JS的在函数中定义的局部变量只对这个函数内部可见,称之谓函数作用域. 嵌套作用域变量搜索规则:当在函数中引用一个变量时,JS会搜索当前函数作用域,如果没有找到则搜索其上层作用域,一直到全局作用域. var va

  • JavaScript原型链详解

    目录 1.构造函数和实例 2.属性Prototype 3.属性__proto__ 4.访问原型上的方法 5.构造函数也有__proto__ 6.构造函数的原型也有__proto__ 7.Object.prototype这个原型对象很特殊 8.总结 1.构造函数和实例 假设你声明一个方法叫做Foo() ,那么我们可以通过new Foo()来声明实例. function Foo() { console.log("我是一个构造方法"); } const f1 = new Foo(); 现在你

  • Java 设计模式之责任链模式及异步责任链详解

    目录 一.定义 二.普通责任链模式 三.异步责任链模式 一.定义 责任链模式(Chain of Responsibility Pattern):避免将一个请求的发送者与接受者耦合在一起,让多个对象都有机会处理请求.将接受请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止. 在很多源码都有涉及,如Mybatis拦截器.Filter- 责任链模式属于行为型模式. 二.普通责任链模式 抽象处理类:AbstractProcessor /** * 抽象处理类 */ public

  • javascript学习笔记(五)原型和原型链详解

    私有变量和函数 在函数内部定义的变量和函数,如果不对外提供接口,外部是无法访问到的,也就是该函数的私有的变量和函数. 复制代码 代码如下: <script type="text/javascript">     function Test(){         var color = "blue";//私有变量         var fn = function() //私有函数         { }     } </script> 这样在

  • JavaScript中原型和原型链详解

    javascript中的每个对象都有一个内置的属性prototype,Javascript中对象的prototype属性的解释是:返回对象类型原型的引用.意思是是prototype属性保存着对另一个JavaScript对象的引用,这个对象作为当前对象的父对象. 复制代码 代码如下: A.prototype = new B(); 理解prototype不应把它和继承混淆.A的prototype为B的一个实例,可以理解A将B中的方法和属性全部克隆了一遍.A能使用B的方法和属性.这里强调的是克隆而不是

  • javascript作用域和作用域链详解

    目录 一.javascript的作用域 1.全局作用域 2.局部作用域 二.javascript的作用域链 三.作用域链和优化 四.改变作用域链 1.with语法改变作用域链 2.catch语法 总结 一.javascript的作用域 1.全局作用域 1.最外层函数和最外层函数定义的变量 var age = 20 function func1() { var sex = "男" function func2() { console.log("hello func2"

  • Javascript变量的作用域和作用域链详解

    工作这几年,js学的不是很好,正好周末有些闲时间,索性买本<js权威指南>,大名鼎鼎的犀牛书,好好的把js深入的看一看.买过这本书的第一印象就是贼厚,不过后面有一半部分都是参考手册. 一:作用域 说起变量第一个要说到的肯定就是作用域,正是因为不熟悉JS的作用域,往往就会把面向对象的作用域张冠李戴,毕竟有些东西总是习惯性的这样,但是并不是每次照搬都是可以的,那么下一个问题就来了,js到底是什么作用域,当然是函数作用域了,我们的浏览器就是一个被实例化的window对象,如果在window下定义一个

  • JS 作用域与作用域链详解

    (1)作用域 一个变量的作用域(scope)是程序源代码中定义的这个变量的区域. 1. 在JS中使用的是词法作用域(lexical scope) 不在任何函数内声明的变量(函数内省略var的也算全局)称作全局变量(global scope) 在函数内声明的变量具有函数作用域(function scope),属于局部变量 局部变量优先级高于全局变量 复制代码 代码如下: var name="one"; function test(){   var name="two"

随机推荐