原稿:语雀 · 问题收录 · 原目录:JavaScript › 问题收录

函数、类、对象??

昨天写继承,发现自己的写的ES6类执行后原型上没有方法,所有方法都在实例上??当时的部分代码如下

class Animal {
  constructor() {
    this.name = "animal";
    this.sex = "male";
  }
  sleep = function() {
    console.log("animals can sleep");
  }
}

class Dog extends Animal {
  constructor() {
    super();
    this.age = 0;
  }
  bark = function(){
    console.log("dogs can bark");
  }
}

执行没有报错,但父类和子类方法直接在子类对象上创建,且原型上啥都没有???🤔

image.png

想了一天后突然发现自己的方法写的有点问题,类块中方法的正确写法应该是sleep(){}、bark(){},改了后果然正常,自己确实经常搞混函数、类、对象的语法

对象

其中存放键值对k:v,每个kv对之间使用逗号间隔,如果是函数可以写成function:XX(),或者ES2015允许的XX()简写,对象中自然不能直接写let = xxx的语句(当然function:XX(){let = xxx ...}是可以的)

函数

JS万物皆对象,函数就是对象,对应构造函数Function

,为什么函数体可以乱糟糟的?因为函数体本身是一个kv对中的v,函数声明就是传入各种参数与函数体来初始化一个函数对象

类是函数,也是对象,类构造器、类块方法、静态方法就是对象的kv对,但是各种方法的写法却限定为函数的简写形式,不能使用function:XX()

我写的sleep = function() {}为什么不报错?因为我无意中创建了类的字段声明(见MDN 类),效果就是每个实例对象上都会创建一份字段声明的属性——因此每个dog都有sleep()和bark()


!!与强转技巧

let a = !!5,
  b = !5;
console.log(typeof a);// boolean
console.log(a);// true
console.log(typeof b);// boolean
console.log(b);// false
// 第一个!强换为boolean类型,但同时取非,再加一个!恢复原boolean值

!!是一个强转技巧,类似的有

强转Boolean

用!!:let a = !!5

强转Number

用+:let b = +"1234"

强转String

用""+ let c = ""+1234


或运算

手写call()遇到let context = context || window,用于在call(null)让this指向window,为什么这么写?

  • JS逻辑运算中:0、""、null、undefined、false、NaN都会判为false,其他都为true
  • 对于(A || B)表达式,当A为false,直接返回B;当A为true,直接返回A

typeof用法

JS中有5种(不计Symbol)原始数据类型:Undefined、Null、Boolean、Number、String

typeof操作符通常用于判断变量的原始数据类型,使用中有以下注意点: 2. typeof对于对象都会返回"object",要确定对象的具体引用类型需要使用instanceof 4. 函数虽然是对象,但typeof func会返回"function" 6. null是Null类型唯一值,表示一个空对象指针/空引用,因此typeof null返回"object"

console.log(typeof null); // object
console.log(typeof []); // object
console.log(typeof NaN); // number
console.log(typeof ""); // string
console.log(typeof undefined); // undefined

闭包的意义

今天写防抖和节流,突然很奇怪为什么JS实现很多功能都要靠函数嵌套返回函数,最后在别处调用,为什么Java就没有这么干?总有种知其然不知其所以然的感觉🤔

在知乎上看了一圈,兜兜转转,回到了闭包。

  • 闭包究竟是什么:函数和函数内部所能访问的变量总和
  • 闭包的究极意义是什么:对外隐藏变量——闭包的一大特性就是内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后
  • 为什么通常闭包都是嵌套函数:想要使用一个变量,又不想被全局其他成员访问,只好声明在函数中,在其内部创建函数,很多时候可以返回内部函数在别处使用

感觉让我理解闭包意义的是这两个帖子:

答主把闭包的特征总结为三点:

  1. 一个依赖于自由变量的函数
  2. 处在含有这些自由变量的一个外围环境
  3. 这个函数能够访问外围环境里的自由变量
image.png

右侧代码的内部函数依赖自由变量y、Add()的词法环境提供了y、JS语法允许内部函数访问y——此时内部函数和Add()的词法环境构成了闭包

如果let calc = Add(3),会获得一个加法器,通过calc(1),calc(2) …可以实现给任何数加3,调用者可以尽情使用calc(),但是无法访问或修改这个固定加数y,从作用域链角度看,当Add(3)执行完毕,它的上下文出栈,词法环境被保留到heap后,这个y就对外隐藏了,用轮子哥的话理解👇

问题收录 配图

为什么Java没有闭包???

根据上面的回答,其实Java处处是闭包,比如对象、内部类等

Java使用new实例化Add对象后,Java对象不会像JS中的函数上下文一样立即消失,同时private又保证了变量对外隐藏,之后只要使用calc.add()就能实现计算

image.png

最后回到JS嵌套函数写法——因为运行机制不同,JS只能这样曲线救国实现数据封装,个人感觉更应该按照Java中的类去理解,外部函数是类,调用外部函数就是创建对象,传入的参数都成为了闭包的一部分,返回的内部函数是对象的方法,可传入参数被调用

比如防抖函数的使用语句content.onmousemove = debounce(count,1000);

  • 创建一个防抖器对象,用于count函数的防抖,要求间隔1000ms
  • 将防抖器对象的内部函数作为onmousemove的处理程序(对象的方法)
  • 每次触发事件,就是执行防抖器上的方法,尽管debounce()的上下文早已经出栈