六个Javascript新手需要注意的事情

我们写这篇文章当然不是说Javascript不好,只是说它有些特性和我们通常理解的面向对象的语言有些差别。而这些差别很有可能和你所想的有很大的差别。但并不是说他们就不好,从另外一个方面来讲,假如你习惯了,也许这也是Javascript独特的魅力。

1. 三个等号

假如你学过别的编程语言,比如Java之类的,你肯定知道一个等号表示赋值,两个等号表示比较。但是在Javascript中,你会发现这里不仅有两个等号,还有三个等号===。我们应该使用哪个呢?他们之间有什么差别的呢?其实两个等号,简单说就是只比较他们的值,而不管他的类型,比如:

if('2' == 2) {} // true

这里会返回true,因为他们的值是相等的,哪怕他们是不同的类型。

再看下面这个例子:

	if('2' === 2) {} // false
	if(2 === 2) {} // true

在三个等号的情况下,上面一个例子会发现’2’和2的类型不同,所以就会直接返回false。也就是说三个等号需要是同样的类型,然后才进行值的比较。

通常来说,会推荐使用三个等号来进行比较。

2. 很多创建对象的方法

在Java或者C#中,你会有一个类,然后基于类创建对象。而在Javascript中,则更加灵活,你可以使用一下方法来创建对象:

  • 使用类:Javascript中也有class,你可以定义相应的方法,域,getter, setter的调用等等。下面就是一个例子:
	class Person {
	  constructor(n) {
	    this.name = n;
	  }
	
	  getName() { return this.name; }
}
  • 对象字面量(Object Literal): 你可以不定义类就创建对象,你只要使用{}就可以了,如下所示:
 const person = {
	    name: 'chris',
	    city: 'location',
	    getAll() {
	      return `${this.name} ${this.city}`;
	    }
	  }
  • 使用 Object create:你可以使用object.create()来创建对象,他是基于现有对象的prototype来创建的。参见下面的例子:
const address = {
	    city: '',
	    country: ''
	  } 
	
	  const adr = Object.create(address);
	  adr.city = 'London';
	  adr.country = 'UK'
	  console.log(adr.city); // London
	  console.log(adr.country); // UK

3. 块语句,看起来好像没有作用域

一些块语句,比如if, for, while等等,并不会创建一个本地的局部域。这也就意味着你在他们里面创建的变量,在他们外面是可以访问的。比如:

	for (var i =0; i< 10; i++) {
	  console.log(i);
	}
	
console.log(i);

这里,console.log(i)会打印10,是不是很惊喜,为什么这个i在for循环之外还能访问呢?假如你去问Javascript的创始人Brendan Eich,他回答你,这就是个feature,哈哈。

假如你想让Javascript这个特性和别的你熟悉的语言类似的话,那么你可以使用let或者const,像下面这样:

	for (let i = 0; i< 10; i++) {
	  console.log(i);
	}
	
console.log(i);

这样之后,你会得到i没有定义的错误。为什么会这样,因为let允许你定义只在某一个块中存在的变量。所以,这就是使用let而不是var的一个典型场景。

4. 上下文中,this的值是什么

你可能听说过一个玩笑,就是根本没有知道this是什么,假如一个空文件,那么this是一个全局的语境。比如下面这个代码:

	global.name = "cross";
	
	function someFunction() {
	  console.log(this.name);
	}
	
	someFunction();

上面代码中,我们把name赋给global变量,这个this的值就从global的语境中得来。

我们再来看下面这个代码:

	var object = {
	  name: 'chris',
	  getName() {
	    console.log(`${this.name}`);
	  }
	}
	
object.getName();

在这里,this就是这个object本身了,这里我们知道name是什么,比如例子中就是chris。

改变语境

我们可以改变this的指向。Javascript中有几个方法可以用来改变this的指向,他们是bind(), call()以及 apply()。看下面这个例子:

	global.name = "cross";
	
	var object = {
	  name: 'chris',
	  getName() {
	    console.log(`${this.name}`);
	  }
	}
	
	function someFunction() {
	  console.log(this.name);
	}
	
	someFunction();

现在someFunction中的this是指向global的,我们可以通过调用上面提到的方法来改变这个this,让他指向object中的this:

	someFunction.bind(object)();
	someFunction.call(object)
someFunction.apply(object)

现在他就会打印出chris而不是cross了。

这三个方法的调用其实还是有一些差别的,不过在我们这里关于this的改变,还是一样的。

this的疑惑点

很多时候,我们会发现很难确定this的值,尤其是使用构造函数来创建一个对象的时候:

	function Person(n) {
	  this.name =  n || 'chris';
	  function getName() {
	    return this.name;
	  }
	  return {
	   getName
	  };
	}
	
	const person = new Person();
	console.log(person.getName()) // undefined 

这里是因为你使用了new函数来创建的时候,this就发生了改变。那如何解决这个问题呢:

方案1 this = that

一个解决这个问题的方法是通过一个that来保存this的值,我们来看下面这样的改变:

	function Person(n) {
	  this.name =  n || 'chris';
	  var that = this;
	  function getName() {
	    return that.name;
	  }
	  return {
	   getName
	  };
	}
	
	const person = new Person();
console.log(person.getName()) // 'chris'

这个方案中就是用that保存了原来的this的值。当然还有别的方法可以解决这个问题:

方案2: 箭头函数

	function Person() {
	  this.name = 'chris';
	
	  const getName = () => {
	    return this.name;
	  }
	
	  return {
	    getName
	  }
	}
	
	const person = new Person();
	console.log(person.getName()) // 'chris'

用箭头函数来替换原来的函数,也是可以解决这个问题的。

方案3:使用闭包

第三种方案是使用闭包。这里我们没有使用new,也没有使用this,代码如下:

	function Person() {
	  var name = 'chris';
	
	  const getName = () => {
	    return name;
	  }
	
	  return {
	    getName
	  }
	}
	
	const person = Person();
console.log(person.getName()) // 'chris'

这个方案中,我们既没有使用new也没有使用this,这大概是最像javascript的使用方法了。

方案4 把方法放到prototype

我们可以这样写:

	function Person() {
	  this.name = 'chris';
	}
	
	Person.prototype.getName = function() {
	  return this.name;
	}
	
	const person = new Person();
	console.log(person.getName()) // 'chris'

这是一个很不错的方法。他不仅仅解决了this的问题,也可以保证这个方法只被创建一次,而不是每一个实例都创建一次。

方案5 使用一个类

代码如下,他和方案4差不多:

	class Person {
	  constructor() {
	    this.name = 'chris'
	  }
	
	  getName() {
	    return this.name;
	  }
	}
	
	const person = new Person();
	console.log(person.getName()) // 'chris'

由于篇幅所限,其实很有很多别的情况会遇到this的问题,这里就不再一一详细介绍了,不过还是希望这篇文章能够抛砖引玉,让你有一些解决相关问题的想法。

5. Const可以工作,但是可能和你想的不太相同

我们都理解const这个关键字,一提到这个关键字,你会想到什么呢?不可以改变等等,对吧?我们来看下面这个例子:

const PI = 3.14 // exactly :)
PI = 3;

假如你这样写,那么就会有这个错误:Assignment to a constant variable

哇,和我们想象的一样耶,开心。OK,那么我们再来看看下面这个例子:

	const person = {
	  name: 'chris'
	}
	
person.name = 'cross'; 

这个代码竟然没有报任何错误,为什么会这样,不是说const表示不能改变吗?其实const只是说这个引用是只读的,也就是说这个引用不能被重新赋值。而不是说他的值不能改变,我们来看下面这个例子:

	const person = {
	  name: "chris",
	};
	
	person = {
	  name: 'chris'
}

这个代码就会报出你想的错误了:Assignment to a constant variable

那我们有没有办法让person不能被改变呢,有的,我们可以使用Object.freeze(),如下所示:

	Object.freeze(person)
	
	person.name = "cross"; 
	
console.log(person.name) // 'chris'

开心吗,我们终于有一个方法可以让他不变了,但是,不要开心得太早,他只会处理第一层的,后面的还是可以改变的,我们来看下面这个代码:

	const person = {
	  name: "chris",
	  address: {
	    town: 'London'
	  }
	};
	
	Object.freeze(person)
	
	person.name = "cross"; 
	person.address.town = 'Stockholm';
	
	console.log(person.address.town) // Stockholm

这个例子中的town还是可以被改变的。那该怎么办呢?可能需要一个深度的freeze函数?也许我们需要仔细考虑一下,我们为什么需要这个,我的意思是大多数时候,我们的const应该是比较初级的变量,而不应该这么复杂,也许这是一个更好的答案。

6. 函数调用之后存在的生命周期

我们来看下面这个代码:

	function aFunction() {
	  let name = 'chris';
	  console.log(name) // prints chris
	}
	
console.log(name)

没有什么特别的,在第二个打印name的地方我们并不知道name是什么,因为他已经在函数外面了。下面我们来稍微修改一下这个函数:

	function aFunction() {
	  let name = "chris";
	  return {
	    getName() {
	      return name;
	    },
	    setName(value) {
	      name = value;
	    }
	  }
	}
	
	const anObject = aFunction();
	console.log(anObject.getName());
	anObject.setName("cross");
	console.log(anObject.getName());

第一个getName返回chris,然后我们调用setName,然后第二个getName会返回cross。咦,咋一看,是这样啊,这就是我想要的啊。但是我们再仔细想一下,我们其实都是调用的函数,而name是其中的局部变量,理论上我们前面使用的setName应该对后面没有影响才对啊。这大概就是Javascript的强大之处了。

参考文章: https://dev.to/itnext/5-things-that-might-surprise-a-javascript-beginner-oo-developer-1nje

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *