Introduction
柯里化(Currying)是一种函数式编程的技术,它是将一个接受多个参数的函数转换为一系列只接受一个参数的函数的过程。这些只接受一个参数的函数被称为柯里化函数。
下面是一个Currying实现的例子,需要好生领悟。
1 | // 使用rest parameters |
柯里化(Currying)是一种函数式编程的技术,它是将一个接受多个参数的函数转换为一系列只接受一个参数的函数的过程。这些只接受一个参数的函数被称为柯里化函数。
下面是一个Currying实现的例子,需要好生领悟。
1 | // 使用rest parameters |
defer attribute in script tag.1 | <script src="path/to/script.js" defer></script> |
async attribute in script tag.1 | <script src="path/to/script.js" async></script> |
window.load event to load script.1 | <script> |
document.DomContentLoaded event to load script.1 | <script> |
平时我们使用console的时候,基本都是console.log(xxx), 其实console的用途不只是打log,今天我们来看看console的其他用法。
1 | console.log(xxx); // print log |
console.time 和 console.timeEnd 可以用来计算代码执行时间,用法如下:
1 | console.time('my-timer'); |
如果在代码执行中打印阶段性时间,可以使用console.timeLog:
1 | console.time('my-timer'); |
console.table 可以将数组或对象以表格形式打印出来,用法如下:
1 | const data = [ |
console.assert 可以用来断言某个条件是否为真,如果为假,则输出错误信息,用法如下:
1 | console.assert(1 === 2, '1 is not equal to 2'); |
输出结果如下:
VM479:1 Assertion failed: 1 is not equal to 2
console.count 可以用来统计某个函数被调用的次数,用法如下:
1 | function greet(user) { |
输出结果如下:
1 | default: 1 |
console.group 和 console.groupEnd 可以用来将输出的内容分组,用法如下:
1 | console.group('group1'); |
console.dir 可以用来打印对象的属性,用法如下:
1 | const obj = { name: 'Alice', age: 18 }; |
console.clear 可以用来清空控制台,用法如下:
1 | console.clear(); |
console.trace 可以用来打印当前调用栈,用法如下:
1 | function foo() { |
types can create primitive type alias, while interface cannot.
1 | type Name = string; |
interface can do declaration merging together
1 | interface Person { |
while type can not do declaration merging
1 | type Person = { |
Extends and implements
1 | class Person { |
Interface extends interface
1 | interface PartialPointX { x: number; } |
Type alias extends type alias
1 | type PartialPointX = { x: number; }; |
Interface extends type alias
1 | type PartialPointX = { x: number; }; |
Type alias extends interface
1 | interface PartialPointX { x: number; } |
types can create interaction types, but interface not.
1 | type Name = { |
types can create union types, but interface not.
1 | type Man = { |
types can define tuple, but interface not.
1 | type Response = [string, number]; // ok |
总结一下,主要的区别就是:
interface可以做declaration合并,但是type不能。interface can be extend by class, while type can not be extend.type 可以定义union interaction类型,但是interface不能。Suppose the following expression
1 | A === B |
How strict equality works in JavaScript? Here are the steps:
falseNaN, return false1 | console.log(1 === '1'); // false, different type. |
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Strict_equality
How to implement lazy load images in html page?
loading=lazy attribute of img tagWith html5, you can use loading="lazy" attribute to lazy load images. This attribute tells the browser to defer loading of the image until it is needed. The browser will load the image when it is about to come into view.
1 | <img src="image.jpg" loading="lazy" alt="描述性文字"> |
For old browsers which do not support loading attribute, you can use JavaScript to implement lazy load images.
1 | <!-- HTML --> |
1 | <script> |
Please note that, window.load event won’t wait for lazy loaded resources.
这篇文章主要介绍一下JavaScript中的比较运算符==。先看几道题,如果你都能答上来,那么可以不用看此文了。
1 | console.log('abc' == String('abc')); |
String('abc') and new String('abc')String(xxx)是将xxx转换为字符串,所以它返回的是字符串,而'abc'原本就是字符串,这里相当于直接返回'abc'。typeof String(‘abc’)返回的是string。new String(xxx)是创建一个字符串对象,其内容是xxx,所以它返回的是一个对象类型,不是字符串类型。typeof new String(‘abc’)返回的是object。== works?==采用Loose equality算法,其过程如下:
true,否则返回false。true。true。注意:+0和-0被认为是相等的,NaN和任何值都不相等,包括其自身。true或者都是false时返回true。true。true。null,另一个操作数是undefined,返回true。primitive类型,另一个操作数是Object类型,将Object类型转换为primitive类型(转换规则在此),再按如下规则比较。primitive类型了,按如下规则继续比较。Symbol类型,但另一个不是,返回false。Boolean类型,将其转换为Number类型(true -> 1, false -> 0),继续往下比较。Number类型与String类型比较,将String类型转换为Number类型进行比较。转换规则在此。Number类型与BigInt类型比较,比较他们的值,如果有一个操作数是+Infinity/-Infinity/NaN,返回false。String类型与BigInt类型比较,将String类型转换为BigInt类型(用BigInt()转换),若转换失败则返回false。类型相同时,比较好理解,我们重点梳理一下类型不同时的比较。
所以,我们重点讨论一下基本类型之间的比较。JS中有7种基本类型,分别是String, Number, BigInt, Boolean, Symbol, null, undefined。
null和undefined之间的比较,返回true。Symbol和其他类型之间的比较,都返回false。String, Number, BigInt, Boolean之间的比较。剩下的这几个类型比较,基本围绕Number进行。Boolean和Number之间的比较,将Boolean转换为Number,然后再比较。String和Number之间的比较,将String转换为Number,然后再比较。Boolean和String之间的比较,将他们都转换为Number然后再比较。String和BigInt之间的比较,将String转换为BigInt,然后再比较。Number和BigInt之间的比较,比较他们的值,如果有一个操作数是+Infinity/-Infinity/NaN,返回false。注意:
Loose Equality算法满足交换律,即A == B等价于B == A。document.all被视为undefined.以下给出一些采用==进行比较的例子,我们会给出每个例子的比较规则对应的序号(参考How == works一节)。
1 | "1" == 1; // true, 2.3.4 |
下面这个例子要注意:
1 | const a = [1, 2, 3]; |
Array.prototype.toString()方法重写了Object.prototype.toString()方法,数组转化为字符串时,会执行以下步骤:
toString()方法,将其转化为字符串。所以上面的例子就是[1, 2, 3].toString() == "1,2,3",所以返回true。
所以空数组和空字符串之间的比较也是true。
1 | [] == ""; // true, 2.3.4 |
数组转字符串还有很多细节,详情请看这里:Array.prototype.toString()。
==比较时,不要老想着转换为Boolean,其实大多数时候是转换为Number==比较时,一定是falsenew String(...)和String(...)完全不同,前者是创建String对象,后者是将...转换为字符串。本来用英语写了一篇,结果看起来乱糟糟的,还是用中文写一下吧。
原型是JavaScript中非常重要的概念,面试中也常常会问到这个问题。那么,原型到底是什么?
原型的英文是prototype,他是函数的一个属性,是一个对象。
下面来看一张图,这个图乍一看非常复杂,但是理解之后,就会发现原型其实非常简单。
在理解这张图之前,我们先明确几个概念:
__proto__和constructor属性。prototype属性。__proto__和constructor属性。所以,我们可以得出以下结论:
__proto__和constructor属性。prototype, __proto__和constructor属性。这两条结论反映在上面的图中就是:
__proto__和constructor属性。prototype, __proto__和constructor属性。我们以一个简单的Person函数阐述上面这张图。
1 | function Person(name) { |
在这个例子中,Person函数是一个构造函数,p是Person的一个实例。为了方便,我们将上图简化一下,只保留__proto__属性。
先看左边纵向的这条线。
p是Person函数的一个实例,所以p的__proto__属性指向Person的prototype属性。Person继承自Object,所以Person的__proto__属性指向Object的prototype属性。Object的__proto__属性指向null。p, Person, Object, null之间通过__proto__属性形成了一条链,这就是所谓的原型链。当我们在某个对象上查找一个属性时,如果该对象没有这个属性,那么JS会通过原型链向上查找,直到找到这个属性或者到达null。举几个例子:
p.printName() - p对象上没有printName属性,所以JS会通过原型链向上查找,找到Person的prototype属性,从而找到printName方法。p.hasOwnProperty('name') - p对象上没有hasOwnProperty属性,向上查找到Person,Person的原型对象上也没有hasOwnProperty属性,继续向上查找到Object,Object原型上有hasOwnProperty属性,于是调用之,所以返回true。p.foo() - p对象上没有foo属性,向上查找到Person,Person的原型对象上也没有foo属性,继续向上查找到Object,Object的原型上还是没有foo属性,继续向上查找到null,而null上也没有foo属性,所以会报错。— TypeError: p.foo is not a function再看右侧部分,Person, Function, Object这三者皆是函数,而JS中函数也是对象,故而这三者亦有__proto__属性,而且都指向Function的prototype属性。而Function的__proto__属性指向Object的prototype属性。
由此我们可以得出结论:
__proto__属性指向其构造函数的prototype属性。__proto__属性指向Function的prototype属性。Function的__proto__属性指向Object的prototype属性。Object的__proto__属性指向null。所有__prototype__的终点都是null.
照例现简化一下上图,只保留prototype属性。
因为prototype是函数的一个属性(对象没有prototype),所以图中只有绿色节点有prototype属性。对于一个函数Foo来说,他的prototype属性是一个对象,指向Foo.prototype
最后来看constructor属性。
constructor就是某个对象的构造函数,因为JS中函数也是对象,所以函数也有constructor属性。
constructor属性指向其构造函数。constructor属性指向Function。Function的constructor属性指向其自身。根据以上结论,不难得出如下结论:
p的constructor属性指向Person函数, 因为p是Person的一个实例。Person.prototype的constructor指向Person函数,这个是为啥?- 硬性规定Person和Object的constructor都指向Function,因为Person和Object都是函数。Function的constructor属性指向Function。由以上结论可以推导出:Function是所有constructor的终点。
我们把上面的结论公式化一下,对于如下代码:
1 | function Person() {} |
我们有如下结论成立:
1 | console.log(p.__proto__ === Person.prototype); // true |
prototype属性,因为箭头函数不可以做构造函数。bind创建的函数)也没有prototype属性。why?prototype is an very important concept in JavaScript. It’s used to implement inheritance in JavaScript.
prototype is a property of a function, it’s an object. When you create a function, JavaScript engine will automatically create a prototype object for you. This object has a property called constructor, which points back to the function.
The prototype data property of a Function instance is used when the function is used as a constructor with the new operator. It will become the new object’s prototype.
Take the following code as an example, its prototype is an empty object {}.
1 | function Person(name) { |
Let’s create a functions on its prototype, and it’s prototype now has a property(function) called printName.
1 | function Person(name) { |
PersonWhen you create an instance of Person, the instance will have a property called __proto__, which points to the prototype of the constructor function(the Person function).
1 | function Person(name) { |
Functions defined on the prototype are shared by all instances of the constructor function.
1 | function Person(name) { |
But, functions defined in the constructor function are not shared by all instances.
1 | function Person(name) { |
require and import?我能想到的点,做性能优化前一定要有benchmark,否则你不知道你的优化是否有效果。
性能优化的前提是知道哪里慢,才能有针对性的优化,所以优化前一定要进行监控,找到嗯提的根源,对症下药。