likes
comments
collection
share

🐵好让人傻傻分不清—JS篇

作者站长头像
站长
· 阅读数 21

前言

前面已经有 🐭好让人傻傻分不清—DOM篇🐯好让人傻傻分不清—CSS篇,怎么能少了 JS 篇呢,接下来我们一起来,JS 中常见的让人分不清的东西吧。

hasOwnProperty / in

hasOwnPropertyin都能检查对象是否包含指定的key属性。

const person = {
    name: 'Li Lei',
};

'name' in person; // true
person.hasOwnProperty('name'); // true

两者的区分点如下:

  • 对于继承的属性,in将返回truehasOwnProperty,顾名思义,检查属性是否本身拥有,而忽略继承的属性。

让我们再看下前一个示例中的person对象。因为对象有内置的属性,如构造函数,proto,下面的结果都会返回true

'constructor' in person; // true
'__proto__' in person; // true
'toString' in person; // true

hasOwnProperty在检查这些属性和方法时会返回false

person.hasOwnProperty('constructor'); // false
person.hasOwnProperty('__proto__'); // false
person.hasOwnProperty('toString'); // false
  • 对于类的getset方法,hasOwnProperty也返回false

例如:

class Person {
    get name() {
        return 'Li Lei';
    }
}

const person = new Person();

虽然namePerson的属性,

person.name; // Li Lei
'name' in person; // true

hasOwnProperty还是会忽略这个属性:

person.hasOwnProperty('name'); // false

instanceof / typeof

instanceoftypeof是都可以用于检查值的类型。 typeof操作符检查值的类型是否为基本类型,可以是布尔、函数、对象、数值、字符串、undefinedsymbol

typeof 'helloworld'; // 'string'
typeof new String('helloworld'); // 'object'

instanceof操作符检查值是否为类或构造函数的实例。

'helloworld' instanceof String; // false
new String('helloworld') instanceof String; // true

构造函数定义方法

对于构造函数通常有两种方式定义方法:

  • 在函数体内定义属性方法
function Calculator() {
    this.sum = function (a, b) {
        return a + b;
    };
}
  • 在函数的原型上定义方法
function Calculator() {}

Calculator.prototype.sum = function (a, b) {
    return a + b;
};

两种方式在通过实例调用该方法时产生相同的结果

const calc = new Calculator();
calc.sum(1, 2); // 3
  • 在第一种方法中,每次创建新实例时都必须创建方法。但是,第二种方法只会创建该方法一次,并将其共享给所有实例,也就是说,第二种方法比第一种方法更有效,需要的内存更少。
  • 通过原型创建的方法可以被扩展和重写。

encodeURI / encodeURIComponent

因为 URL 只能由标准 ASCII 字符组成,所以必须对其他特殊字符进行编码 ,它们应该要被 UTF-8 编码的不同字符。

  • encodeURI用于编码完整的 URL。
encodeURI('https://domain.com/path to a document.pdf');

// 'https://domain.com/path%20to%20a%20document.pdf'
  • encodeURIComponent用于对 URI 组件(如查询字符串)进行编码
`http://domain.com/?search=\${encodeURIComponent('encode & decode param')}`;

// 'http://domain.com/?search=encode%20%26%20decode%20param'

有 11 个字符不能由encodeURI编码,但是能由encodeuriccomponent编码。

字符encodeURIencodeURIComponent
##%23
$$%24
&&%26
++%2B
,,%2C
//%2F
::%3A
;;%3B
==%3D
??%3F
@@%40

Map / WeakMap

MapWeakMap 是两种数据结构,都可用于操作键和值之间的关系。 两者的区分如下:

  • 我们可以将对象或任何原始类型用于 Map 的键和值。但WeakMap只接受对象,不能将原始类型当做键。
const attrs = new WeakMap();

// ERROR
attrs.set('color', 'red');
  • Map不同,WeakMap不支持键和值的迭代。不可能获得WeakMap的所有键或值。此外,也没有办法清除WeakMap
  • 当没有引用时,WeakMap会被垃圾回收。而Map一旦被创建,它们将占用内存,不会被垃圾回收,即使没有对它们的引用。这可能会导致内存泄漏问题。
let id = { value: 1 };

const people = new Map();
people.set(id, { name: 'Foo', age: 20, address: 'Bar' });

// Remove the id
id = null;

在删除键对象id之后,仍然能够通过键访问它的引用。

people.keys().next().value; // { value: 1 }

slice / splice

  • slicesplice是都能截取给定数组子数组的常用方法。
array.slice(startingIndex, endingIndex);
array.splice(startingIndex, deleteCount, ...items);

两个方法的第一个参数都是截取的开始位置索引 第二个参数分别是截取的结束位置索引和元素的数量(长度) 对于splice第三个以及后续的参数是可以在开始位置索引上新增的数组子项

  • splice是会改变原数组,slice不会改变原数组
const array = [1, 2, 3, 4, 5];
const sub = array.splice(2, 3);

// 原数组被修改
array; // [1, 2]
sub; // [3, 4, 5]
const array = [1, 2, 3, 4, 5];
const sub = array.slice(2, 3);

// 原数组不会修改
array; // [1, 2, 3, 4, 5]
sub; // [3]

apply / call / bind

applycall都是调用函数的常见方法,而bind是创建一个新的方法。 三者间入参会有所不同:

fn.apply(thisArg, argsArray);
fn.call(thisArg[, arg1[, arg2[, ...]]]);
fn.bind(thisArg[, arg1[, arg2[, ...]]]);

例如,下面的函数返回三个数字的和:

const sum = (a, b, c) => a + b + c;

sum.apply(null, [1, 2, 3]); // 6
sum.call(null, 1, 2, 3); 		// 6
sum.bind(null, 1, 2, 3)();	// 6

结束语

至此,傻傻分不清告一段落,老铁们,有问题留言互相交流哇。