匿名函数
//匿名函数:没有名字的函数
//常见的使用方式
//1.
document.onclick = function(){
}
//2.
let f = function(){
}
//3.
setInterval(function(){
},1000);
// 由以上得出结论->函数也是一种对象
// 目的了解函数的作用
// 特殊函数使用方法
// a.回调函数->一个被当做参数的函数
// b.函数也可以当做函数的返回值->闭包的前置条件
自运行:
函数在定时时,同时被调用,只针对匿名函数
a、方法一
(funtion(){console.log(“今天星期一嘤嘤嘤”);})();
b、方法二
(funtion(){
console.log(“heihei”);
}());
c、关键字
void function(){
console.log(“1111”);
}();
! function(){
console.log(“222”);
}();
闭包的概念
1、闭包的概念:函数嵌套函数,被嵌套的函数称为闭包函数
2、闭包的作用:可以在函数体外使用局部变量
3、闭包的实现:在父函数f1中定义的局部变量count,和子函数f2
在f2中操作沙盒变量count,将子函数f2作为父函数f1的返回值,在外界通过全局变量f绑定f1的返回值,从而实现了闭包
4、闭包的缺陷:闭包会打破垃圾回收机制,可能会造成内存泄漏
//第一种
functio f1(){
var count = 0;
var f2 = function(){
count++;
return count;
}
return f2;
}
let f = f1();//f1()== f2 ==f
console.log(f());
console.log(f());
console.log(f());
//第二种
let f = (function(){
var count = 0;
return++count;
}
}()};
console.log(f());
console.log(f());
闭包的作用-沙盒
背景:类的设计中,属性和方法是有共有和私有的要求,通常来说,所有的属性都是私有,所有的方法都是共有,外界修改私有属性应该只能通过类提供的共有方法实现
沙盒的概念->类的公有和私有的概念->块级作用域
强调get和set方法,对私有属性的修改
垃圾回收机制
对象在创建后需要销毁空间,销毁变量空间的工作是由垃圾回收机制完成的,称为GC机制。
1、标记法:一个变量进入该作用域给一个标记,离开该作用域收回标记,然后销毁
2、引用计数法:在程序中任何一个位置使用到了该变量则计数器+1,停止使用该变量时计数器-1
优点:简单,概念清晰,效率高
缺点:遇到循环引用无法实现
函数的柯里化
只有一个参数,且返回值为一个函数的函数,称为柯里化函数
作用:
柯里化实际是把简单的问题复杂化了,但是复杂化的同时,我们在使用函数时拥有了更加多的自由度。
而这里对于函数参数的自由处理,正是柯里化的核心所在。 柯里化本质上是降低通用性,提高适用性。
function fun(regExp){
return function(str){
return regExp.test(str);
}
}
//验证用户名
let regUserName = fun(/\D\w{5,17}/);
console.log(regUserName("laowang"));
console.log(regUserName("123"));
//验证密码
let regUserPwd = fun(/.{6,}/);
console.log(regUserPwd("heihei"));
console.log(regUserPwd("12"));
函数对象
// JS中万物皆为对象,函数当然也是一个对象
// 函数对象是什么类型的?->引用类型->new
//函数定义的方式
//a.
// function f1(){
// }
//b.
// let f2 = function(){
// }
//c.构造方法创建函数
// let 函数名 = new Function(["参数列表"],"代码块");
// let f3 = new Function("console.log('abc')");
// f3();
// let f4 = new Function("a","b","console.log(a+b)");
// f4(1,3);
arguments:函数的内置对象,作用域只能出现在函数体内(用来保存函数形参的数组)
用来保存函数形参的数组,它是一个伪数组,只能进行数组元素的读,和使用length属性,其他的API不能使用
a、定义不定参函数
function fun() {
// console.log(arguments);
for(let i=0; i<arguments.length; i++){
console.log(arguments[i]);
}
}
fun(1,"嘿嘿嘿",2);
案例:
function fun(){
for(let i=0; i<arguments.length; i++){
if(typeof arguments[i] == "number"){
console.log("执行数字操作");
}else if(typeof arguments[i] == "string"){
console.log("执行字符串操作");
}else if(typeof arguments[i] == "boolean"){
console.log("执行布尔操作");
}
}
}
fun(true,2);
b、递归中如果函数名改名,可以不用改函数内部的名字(arguments.callee:代表函数对象本身)
arguments.callee:代表函数对象本身
function fun(){ // fun == arguments.callee
// console.log(fun);
console.log(arguments.callee);
}
fun();
this
this:函数体内的内置对象->作用域必须在函数体内
//1.与事件体连用,代表触发该事件的元素
document.onclick = function(){
console.log(this);
}
//2.与构造函数连用,代表new出来的对象
function Student(name){
this.name = name;
console.log(this);
}
let s = new Student("蔡徐坤");
//3.与普通函数(除了构造函数和事件体)连用,代表调用该函数的对象
function fun(){
console.log(this);
}
fun();
let data = {
"a":1,
"fun":function(){
console.log(this);
}
}
data.fun();
//4.与箭头函数连用,代表其父元素的前缀
document.onclick = ()=>{
console.log(this);
}
let data = {
"a":1,
"fun":()=>{
console.log(this);
}
}
data.fun();
prototype
前言:类对象在创建时,属性属于每一个对象,所以要开辟独立的空间,而行为或者方法属于整个类族,所有该类的对象都共享该方法,不应该为每一个对象都开辟共享方法的空间
prototype:原型对象,每一个函数(主要强调的是构造函数)都有一个prototype属性,prototype对象存储这该类所有实例对象共享的属性和方法
实例对象不能修改原型属性的值,如果一个实例对象这么做,等于为该实例对象添加了一同名个自定义属性
为什么实例对象可以访问所有的属性和方法?->原型图
实例对象的自定义属性是直接在堆上开辟空间的,所以可以直接访问,每一个实例对象都有一个__proto__属性,该属性指向类的原型对象,console.log(Student.prototype == s1.proto);所以实例对象可以访问原型对象上的属性和方法
apply和call
apply和call是函数对象的成员函数,可以改变函数的this指向,
函数对象.apply(被修改的this指向,[参数1,参数2…]);
函数对象.call(被修改的this指向,参数1,参数2…);
let d = new Dog("旺财");
eat.apply(d,["狗粮","火腿"]);
eat.call(d,"蛋糕","骨头");
let c = new Cat("Tom");
eat.apply(c,["Jerry","鱼"]);
eat.call(c,"猫粮","猫条");
------------------------------------------------
apply,call,bind的异同?
1.都是用来改变this指向的
2.bind通常用来修改匿名函数
document.onclick = function(){
this.xxxx
}.bind(xxx);
3.apply和call通常改变有名函数
4.apply的第二个参数是数组,call不是,bind的参数和call一毛一样
5.apply和call会直接调用该函数,而bind只是产生了一个新的函数对象
继承
继承:子类继承父类派生的属性和方法,可以直接使用。子类还可以添加自己新的属性和方法。
作用:提高了代码的复用性
继承的类型:
一、原型继承
原型继承:通过原型对象prototype实现继承,
如何实现继承,用子类的原型对象指向父类的实例对象
语法为: 子类.prototype = new 父类();
原型继承的缺陷:
1.必须先实现继承关系,才能添加原型对象上的方法或属性
2.一旦实现原型继承关系,子类原型对象的值就不能改变
3.原型继承无法初始化由父类派生给子类的属性
凭什么子类对象可以访问所有的属性和方法?->原型链
子类实例对象可以直接访问new出来堆空间的属性,
子类实例对象通过__protp__指向子类的原型对象,所以可以访问子类原型对向上的方法或属性,
子类的原型对象指向其父类的实例化对象,所以可以访问父类的属性,
父类实例化对象的__proto__指向其父类的原型对象,所以可以访问父类原型对象上的方法或属性
向上找->先在自身找,找不到往上找
二、apply和call继承
apply和call继承,也称为借用构造函数"继承"
好处:可以实现由父类派生给子类的属性,在构造时可以初始化
缺点:无法继承原型对象上的属性或方法
三、混合继承
//混合继承:
//用原型继承,继承原型对象上的方法或属性
//用借用构造方法继承,继承父类派生给子类的属性
function Human(name,id){
this.name = name;
this.id = id;
}
Human.prototype.eat = function(){
console.log("Animal eat");
}
function Student(name,id,score){
//实现属性继承
Human.call(this,name,id);
this.score = score;
}
//实现原型对象继承
Student.prototype = new Human();
Student.prototype.study = function(){
console.log("study");
}
let s = new Student("老王",1,100);
console.log(s.name,s.id,s.score);
s.eat();
s.study();
四、ES6继承
<script>
class Human{
constructor(name,id){
this.name = name;
this.id = id;
}
eat(){
console.log("eat");
}
}
// 通过extends关键字实现继承
// class 子类 extends 父类{
// }
class Student extends Human{
constructor(name,id,score){
//super是代替父类构造函数的关键字,等价于调用父类构造函数
//super必须写在构造函数的第一行
super(name,id);
this.score = score;
}
study(){
console.log("study");
}
}
</script>
设计模式
类的一种设计方案
目的:根据不同的场景,大家统一思想,用相同的设计方案去解决该问题。从而实现程序的可持续性,维护性。
定义:模式是在一个上下文中,对一个问题的解决方案。即模式的四要素:名字、上下文、问题、和解决方案
为什么使用设计模式:
1、尽量用一种标准的方式描述设计经验
2、为设计者提供一种通用的语言
3、增加复用性,减少设计的多样性
4、增强设计变更的灵活性
5、提高设计文档的质量
6、增强设计的可理解性
策略模式
策略模式是行为模式的一种,在例如java语言的表现是通过处理对象和行为的关系,通过多态性来消除if-else分支,而每一个分支都算是一个策略,以此来达到统一的变化。而在js中,多态性是自然而然就存在了,下面将以js的方式演示如何使用策略模式。策略模式为用户提供了自己编写if。。。else的机会
class Calc{
constructor(){
//保存策略的地方(if或else的各种条件)
this.data = {
// "add":function(a,b){
// return a + b;
// }
}
}
//添加策略内容
//name是添加函数的名字
//opt其实就是分支
addFun(name,fun){
this.data[name] = fun;
}
//添加好功能的调用
fun(name,a,b){
// add(1,2)
return this.data[name](a,b);
}
}
let c = new Calc();
c.addFun("add",function(a,b){
return a + b;
});
c.addFun("min",(a,b)=>a-b);
let x = c.fun("add",1,2);//add(1,2)
console.log(x);
x = c.fun("min",2,5);
console.log(x);
观察者模式
观察者模式:也称为订阅发布者模式,该模式由两个类构成,一方是发布者(主题),另一方是观察者,也称为(订阅者).实现的功能最常见就是订阅公众号,凡是订阅了该主题的观察者,在观察者发出消息后,都会接收的该消息。
实现:
订阅者写函数定义
发布者写函数调用