关于JavaScript基础知识的一些理解

简介:关于很多知识点并没有深入讨论,主要是为了梳理一下js的基础概念以及容易和Java混淆的概念。

目录

1.数据类型和变量 ---- 归纳数据类型,整理js中"类"和"实例"的概念
2.函数基本概念 ---- 函数声明,函数表达式,匿名函数,立即执行
3.预解析 ---- 作用域及var 和 function 声明的提升
4.js加载的时间线

1 数据类型和变量

js中的数据类型分为基础数据类型(值类型)对象类型
1基础数据类型:又称为原始类型,在js中有这样五种:
number
string
boolean
undefined
null
2对象类型:object。在js中,数组和函数(array和function),都被当成了一种对象,也就是说,他们都是object类型的。
这里需要注意的是,js中是没有类的概念的,但是是有构造函数的概念。所以说,我们的对象,都是由构造函数创造出来的。而构造函数的那个名字,也常常被称为该对象的"类"。既然没有类的概念,那对象的属性和方法都是怎么来的呢?在js中,采用的是直接在构造函数中声明的方式来构造的,比如:

function Person() {
	this.name = "Tom";
	this.age = 19;
	this.sayHello = function() {
		console.log("Hello! I'm:" + this.name);
	}
	var p1 = new Person();
    p1.sayHello(); // Hello! I'm:Tom
}

这样我们就会说,p1是一个Person类的实例。而p1的数据类型,依然可以说是"object"。
在js中,虽然我们会把上文的Person称为一个类,但我们还是不要把它按照Java的思想,把Person当成一种数据类型,而仅仅是看成一个函数——一个构造函数而已。不然以后可能会有一些比较难解释的地方。比如说Object这个构造函数,我们所有的引用类型,可以说都是Object这个构造函数的实例(比如p1 instanceof Object就会返回true),但是p1并不是Object类,而是object类(js中也没有Object类,只有Object构造函数)。

小结:
Java中,a对象如果是A构造函数构造出来的,那么就可以说a对象是A类型或其父类的实例,也可以说a对象的数据类型是A或其父类。
JavaScript中,a对象如果是A构造函数构造出来的,那么a对象是A类型或其’父类’的实例,但是a对象并不是A类型,js中的类型,只有上述六种(ES6以后新加入的类型没有算在其中)。
当然,在不引起歧义的时候,说a是A类型的对象,也不能算错。

其次,关于Typeof得到的相应结果,可以参考下图:原图链接

在这里插入图片描述

2 函数

如果说object类型在js中是一把手,那么object旗下的function,就是二把手了。函数在js中的地位比在Java中高不少。
函数的基本声明如下:

function f() {
	// 因为js是弱类型,所以函数的返回值可以不指定
}

调用函数和Java一样,直接写f();就可以调用了。
又因为js中函数也是一种对象,所以它也可以被变量指向,如:

var foo = f;
// 调用foo();和f();就是等价的了

如果代码写成:

var foo = f();

这里函数就会被执行一次,并且把返回值赋给b(没有返回值的话b就是undefined类型)。
关于函数传参的问题,如果少传了,那么缺少的形参就是undefined类型,如果传多了,可以使用函数自带的arguments数组进行相应的处理。

js中函数还有一种使用方式,就是使用函数表达式

var f = function() {
    alert(1);
}
f(); // 调用f函数

看起来就像是把函数的命名换了个位置,代表的意思是定义了一个变量f,不过f的数据类型还是函数(这里的函数只是声明,不会运行,想要运行的话还是就和之前一样,调用就行了)。
这样也导致了一个是var关键字声明,一个是function关键字声明,这在后面提到的预解析中,就会产生区别。其次就是在立即执行上,也会存在差异。


除此之外,函数还有一种匿名写法:

function() {
    alert(1);
}

这样就会导致我们不可以调用函数了,那么该函数怎么执行呢?答案就是使用函数的立即执行。(匿名函数因为没法调用,所以必须立即执行)。
函数立即执行的语法有如下两种:
(f(){})();(f(){}());,并且这两种方式功能上并没有什么区别。
匿名函数立即执行的意义在于函数执行且只能执行一次,这样就保证了一些安全性和隔离性。(普通声明的函数也可以立即执行,并且执行过后,也无法执行第二次)。
关于闭包,可以参考:js函数闭包
关于函数原型方面的总结,可以看这一篇文章:js函数原型和原型链

3 预解析

js中也分全局变量和局部变量(我们这里讲的局部变量都是指函数中的局部变量)。
先来看一个特殊的情况:

function foo() {
	x = 0;
}

这里的x变量,理应是局部变量,但因为没有写成var x = 0;所以解析器会把x自动当成全局变量。
而且js里的作用域,也并不是写什么就是什么的,而导致这样的原因,这就要从他的预解析机制说起了。
我们来看这样一段代码:

console.log(x);
var x = 1;

按照以前的思想,第一行代码应该报找不到变量x的异常——ReferenceError: Can't find variable: x,但是我们实际得到的结果是undefined,这是因为,我们的解释器,把js中所有的var声明的变量,进行提升,放到代码的最上面,也就是说,代码其实是长这个样子的:

var x;
console.log(x);
x = 1;

对于函数的声明,我们同样也会有这样的提升:

f();
function f() {
    alert(1);
}

这段代码在解释器眼中,实际是这样的:

function f() {
    alert(1);
}
f();

对于函数内部,局部变量同样会有提升,不过是提升到函数的最前方,如:

function f() {
	alert(y);
    var y = 1;
}

就会被提升成:

function f() {
	var y;
	alert(y);
    y = 1;
}

要注意的是,如果是前面提到的那种特殊情况:

function foo() {
	x = 0;
}

虽然这个x是全局变量,但是他被提升的位置并不是最上面,而是foo这个函数执行之前。比如:

console.log(x);
//var x;解释器会把x提升到f函数执行的前一行,而不是最上面
f();
function f() {
    x = 1;
}

这里的console.log(x)就会弹出找不到’x变量’的异常。

小结
首先会去找var和function声明的变量,然后把其声明提升上来。对于函数体,其内部也会做类似的事情。如果变量名和函数名相同,则优先函数。

4 js载入阶段的过程

  1. 获取页面内容
  2. 加载并解析DOM树
  3. 渲染,下载文件
    我们这里着重关注一下第二点——加载并解析DOM树。因为我们的
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo</title>
</head>
<body>
    <div id="d1"></div>

    <script>
        var a = document.getElementById("d1");
        var b = document.getElementById("d2");
        console.log(a);
        console.log(b);// null
    </script>

    <div id="d2"></div>
</body>
</html>

在浏览器的控制台中,结果如下:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值