day-033-thirty-three-20230322-闭包-this-JavaScript设计模式-面向对象-构造函数设计模式
闭包
闭包选项卡
-
方法1: 又添加了一个自执行函数
-
以前的i来自于全局作用域
-
目前的i来自于自执行函数的作用域
for (var i = 0; i < lis.length; i++) { // 之前i找到的上级作用域是window,现在我们手动增加一层作用域,用一个闭包的形式,里面把点击事件赋值 //给了外面的元素,被占用,形成不销毁的作用域.n是私有变量,当点击页面上的元素的时候,就会找闭包作用域中的私有变量n (function (n) { lis[n].onclick = function () { change(n); }; })(i); }
for (var i = 0; i < lis.length; i++) { lis[i].onclick = (function (n) { return function () { change(n); }; })(i); } for (var i = 0; i < lis.length; i++) { //每次for循环,就给li绑定一个点击事件,并且点击的事件的值是return里面的小函数,形成了不销毁的作用域 //当我们点击li的时候,里面的小函数就会执行,变量i就是自执行函数里面的私有变量 lis[i].onclick = (function (i) { return function () { change(i); }; })(i); }
-
-
方法2: 使用let产生块级作用域
for (let i = 0; i < lis.length; i++) { lis[i].onclick = function () { change(i); }; }
<script>
<div class="main" id="main">
<ul>
<li class="current">音乐</li>
<li>电视</li>
<li>综艺</li>
</ul>
<div class="current">音乐内容</div>
<div>电视内容</div>
<div>综艺内容</div>
</div>
var lis = main.getElementsByTagName("li");
console.log(lis);
for ( var i = 0; i < lis.length; i++) {
lis[i].onclick = function(){
console.log(i);
}
}
</script>
let产生作用域
- 块级作用域:在es6语法中,用{} 括起来的,里面有const 或者let 都是块级作用域:
- 在块级作用域外面访问不到里面的变量
- 块级作用域也有作用域链(块级作用域也可以进行嵌套)
{
console.log(a); //访问不到a,会报错,暂时性死区;
let a=8;
console.log(a); //隐藏暂时性死区报错后。访问得到a;
}
console.log(a); //隐藏暂时性死区报错后。访问不到a,报错a is not defined;
{
let a=2;
{
let b=3;
console.log(a); //访问得到a;
}
}
console.log(a); //访问不到a,报错a is not defined;
this
-
全局作用域下
-
非严格模式下 this—window
console.log(this);
-
严格模式下 this—window
"use strict"; console.log(this);
-
-
普通函数执行
-
非严格模式下 this—window
function show(){ console.log(this) } show();
-
严格模式下 this—undefined
"use strict"; function show(){ console.log(this) } show();
-
-
回调函数执行
-
非严格模式下 this—window
function show(f){ f() } show(function(){console.log(this)});
var arr=[1,2,3] arr.map(function(){ console.log(this)//arr })
-
严格模式下 this—undefined
"use strict"; function show(f){ f() } show(function(){console.log(this)});
"use strict"; var arr=[1,2,3] arr.map(function(){ console.log(this)//arr })
-
两个特殊函数: setTimeout() setInterval() – 都指向window
setTimeout(function(){ console.log(this); },1000)
"use strict"; setTimeout(function(){ console.log(this); },1000)
- 可以理解为,定时器的回调函数执行环境都是在经过一定时间后,在全局环境上执行。
-
-
-
自执行函数
-
非严格模式下 this—window
(function(){ console.log(this); })() console.log(this);
-
严格模式下 this—undefined
"use strict"; (function(){ console.log(this); })()
-
-
事件绑定 this指向的是当前绑定的DOM元素对象
-
非严格模式下 this—当前绑定的DOM元素对象
document.body.onclick=function(){ console.log(this,1);//document.body }
-
严格模式下 this—当前绑定的DOM元素对象
"use strict"; document.body.onclick=function(){ console.log(this,2);//document.body }
-
-
函数执行,前面有点号,点号前面的内容是谁,this就是谁。即函数属于某个对象的一个属性。
-
非严格模式下 this—属于那个对象this就是谁
var obj={ name:"lili", fn:function(){ console.log(this) } } obj.fn() Array.prototype.show()//this-->Array.prototype
-
严格模式下 this—属于那个对象this就是谁
"use strict"; var obj={ name:"lili", fn:function(){ console.log(this) } } obj.fn() Array.prototype.show()//this-->Array.prototype
-
-
箭头函数没有this,指向
上级上下文中的this
var obj={ name:"lili", fn:()=>{ console.log(this) } } obj.fn()//window "use strict"; var obj={ name:"lili", fn:()=>{ console.log(this) } } obj.fn()//window
-
构造函数中的this,指向
实例对象
-
call()、apply()、bind()可以修改this的指向
对象中函数this指向内存图
var num = 10;
var obj = { num: 20 };
obj.fn = (function (num) {
this.num = num * 3;
num++;
return function (n) {
this.num += n;
num++;
console.log(num);
}
})(obj.num);
var fn = obj.fn;
fn(5);
obj.fn(10);
console.log(num, obj.num)
var name="珠峰培训";
function fn(){
console.log(this.name)
}
var obj={
name:"你好世界",
fn:fn
}
obj.fn();//this--》obj "你好世界"
fn();// this-->window "珠峰培训"
(function(){//this-->window
this.fn();// window.fn()-->"珠峰培训"
})();
let obj={
name:"li",
fn:(function(n){
// 这里的this---window
console.log(this);
return function(){
// 这里的this---obj
console.log(this);
}
})(10),
}
obj.fn();//this-->obj
箭头函数不能用于构造函数
因为箭头函数没有原型对象prototype
示例
JavaScript设计模式
-
单例设计模式: 单独的个体不会被相互干扰
var name="lili"; var age=18; //数据会相互影响 var name="Tom"; var age=20;
-
简单单例设计模式—对象
-
用一个对象把需要的方法或属性等数据包在一起
// var name="lili"; // var age=18; // var name="Tom"; // var age=20; var obja={ name:"lili", age:18 } var objb={ name:"Tom", age:20 }
-
-
高级单例设计模式—闭包
-
自执行函数返回一个对象,对象里面把需要的方法或属性等数据包在一起
//高级单例设计模式---闭包 let lunbo=(function(){ function show(){ console.log("轮播") } return { show } })() lunbo.show(); let pubu=(function(){ lunbo.show(); function show(){ console.log("瀑布") } return { show } })() pubu.show();
-
-
-
工厂设计模式: 批量创建内容,封装函数返回一个新的对象,对象里面把把需要的方法或属性等数据包在一起
function CreateObj(name,age){ const res = { name:name, age:age } return res } let obja=CreateObj("lili",18) let objb=CreateObj("Tom",20)
function CreateObj(name,age){ //.... return {//es6 新增 key和value相同可以写一个 name, age } } let obja=CreateObj("lili",18) let objb=CreateObj("Tom",20)
-
构造函数设计模式
-
发布订阅设计模式
-
观察者设计模式
-
承诺者设计模式…
面向对象
- 面向对象是一种编程思想,JavaScript就是基于这个思想构建出来的一门编程语言
- 围绕对象,类(构造函数),实例
- 创建对象的方式
- 构造函数法 new Xxxx()
- 所有的引用类型,基本上都可以用这种来创建
- 字面量法,就是构造函数法的简写
- Math对象比较特殊,不用new也不用字面量法。是一个内置对象
- 能够new出来就是引用对象
- 一些值类型如number在使用时进行了装箱
- 在使用时把值变成对应的引用类型(包装对象),但使用后又变成了值对象
- 拆箱(隐式转换)
- 引用类型在使用时会转化为基本数据类型进行运算,运算完成后,又变成了引用类型
let arr=[];console.log(arr+10,arr)//10 , []
- 引用类型在使用时会转化为基本数据类型进行运算,运算完成后,又变成了引用类型
- 一些值类型如number在使用时进行了装箱
- 构造函数法 new Xxxx()
- 对象: 万物皆对象-----如: 引用类型+DOM+BOM+元素
- 有些基本数据类型不是对象,但使用时会变成包装对象
- 类/构造函数: 根据不同的特征进行划分
- new的什么,就叫什么类
- 类也是一个对象,类型是函数。
- 也存在属性和方法,都以键值对的形式存在
- 上面的方法叫做静态属性和静态方法
- 必须用console.dir()或console.log([类名])详细输出类才会看到
- console.dir()输出的是对象的键值对
- 必须用console.dir()或console.log([类名])详细输出类才会看到
- 上面的方法叫做静态属性和静态方法
- 也存在属性和方法,都以键值对的形式存在
- 类也叫构造函数,基本上能用new调用就是构造函数
- 它也是函数
- 构造函数上的属性和方法就叫静态属性和静态方法
- 实例对象: 类中的具体某个成员
- 也叫实例对象,也就是用new调用构造函数后返回回来的对象
- 同一个类的不同成员,每个实例对象之间
- 具有不同的属性和方法(私有属性和公有方法),
- 相同属性和相同方法(公有属性和公有方法)
- 公有属性或方法的一般都放在[[Prototype]]上
函数对象的画图
- 内部的内容
- 作用域
- 函数体字符串
- 键值对
- 在堆内存上
使用静态属性和方法
-
使用静态属性和方法
-
只能在
构造函数
/类
上使用//如何使用静态属性和方法 : 只能类使用 // isArray静态方法---》判断是不是数组 是 true 不是 false console.dir(Array); console.log(Array.isArray([11]));
-
-
使用私有属性、私有方法、公有属性和公有方法
-
在实例上使用
// 私有有属性和方法 公有属性和方法:实例 let arr = [1, 2, 3]; console.log(arr); console.log(arr.length); arr.push(10); console.log(arr);
-
JavaScript内置类
- JavaScript中的内置类就是在JavaScript里一开始就存在的类,不用声明或创建就能用的类
- 基本上每一个数据类型值都有一个自己所属的内置类
- Number类(每一个数字都是他的实例)、String类、Boolean类、Symbol类、BigInt类、Object类、Array类、RegExp类、Date类、Error类、Function类…
- 每一个元素标签都有一个自己所属的内置类
- 基本上每一个数据类型值都有一个自己所属的内置类
console.dir(document.createElement('div'))
//HTMLDivElement -> HTMLElement- -> Element -> Node -> EventTarget -> Object-->null
console.dir(document.createElement('p'))
//HTMLParagraphElement -> HTMLElement- -> Element -> Node -> EventTarget -> Object-->null
console.dir(window)
//Window -> Window --> WindowProperties -> EventTarget -> Object-->null
构造函数设计模式
- 构造函数设计模式
- 普通函数执行,前面加上new就是构造函数设计模式,就是构造函数设计模式
- 规范一般要求首字母大写
- 小写也不报错,但规范最好要遵守
- 执行步骤
- 创建一个EC(function)函数执行上下文
- 函数执行上下文
- 创建一个EC(function)函数执行上下文
- 规范一般要求首字母大写
- 构造函数执行和普通函数执行有四大区别
- 普通函数执行,前面加上new就是构造函数设计模式,就是构造函数设计模式
函数没小括号
- 以普通函数方式执行,就是函数前面没new就直接调用函数了
- 如果函数后面不接小括号,函数直接就是不执行,返回的是函数本身
- 以构造函数方式执行,就是函数前面有new再调用函数
- new后接构造函数名,构造函数后面可以有小括号也可以没有小括号
- 有小括号,构造函数调用时有入参
- 有小括号时,优先级为18
- 没有小括号,函数依旧会执行,但是相当于不传递函数的new模式调用
- 构造函数没小括号,虽然也会执行,但优先级为17
- 有小括号,构造函数调用时有入参
- new后接构造函数名,构造函数后面可以有小括号也可以没有小括号
function Fn(x, y) {
let sum = 10;
this.total = x + y;
this.say = function () {
console.log(`我计算的和是:${this.total}`);
};
// return {name:"lili"};
}
let res = Fn(10, 20); //以普通函数方式执行。有小括号,正常执行
let res1= Fn;//以普通函数方式执行。没有小括号,函数不执行
let f1 = new Fn(10, 20); //以构造函数方式执行。有小括号,有入参,传递参数 。 优先级18
let f3 = new Fn(); //以构造函数方式执行。有小括号,没有入参,不传递参数。 优先级18
let f2 = new Fn;//以构造函数方式执行。没有小括号,不传递参数。 优先级17
- 函数后面有小括号,优先级为18。
function Fn(x, y) {
let sum = 10;
this.total = x + y;
this.say = function () {
console.log(`我计算的和是:${this.total}`);
};
// return {name:"lili"};
}
let res = Fn(10, 20); //普通函数执行
let f1 = new Fn(10, 20); //构造函数执行
- 同一构造函数,多次调用每次返回的实例一般都不一样
function Fn(x, y) {
let sum = 10;
this.total = x + y;
this.say = function () {
console.log(`我计算的和是:${this.total}`);
};
// return {name:"lili"};
}
let res = Fn(10, 20); //普通函数执行
let f1 = new Fn(10, 20); //构造函数执行 传递参数 优先级18
let f2 = new Fn;//函数执行 不传递参数 优先级17
console.log(f1);//实例
console.log(f2);//实例
- 构造函数里返回的对象,里面的数据要在this上创建键值对
- 返回出去后才能在结果对象中通过对象中括号语法或点语法访问得到
function Fn(x, y) {
let sum = 10;
this.total = x + y;
this.say = function () {
console.log(`我计算的和是:${this.total}`);
};
// return {name:"lili"};
}
let f1 = new Fn(10, 20); //构造函数执行 传递参数 优先级18
console.log(f1);//实例
console.log(f1.sum); //undefined
console.log(f1.total); //30
- 在构造函数内部新生成函数,绑定到this上时,反而返回出去,就算代码一样,所指向的内存地址也是不同的,是两个不同的函数,只是函数内部的代码运行逻辑一样而已
function Fn(x, y) {
let sum = 10;
this.total = x + y;
this.say = function () {
console.log(`我计算的和是:${this.total}`);
};
// return {name:"lili"};
}
let f1 = new Fn(10, 20); //构造函数执行
console.log(f1);//实例
console.log(f1.say===f2.say);//false