html5与javascript(HTML-JavaScript基础看这一篇就够了)
HTML-Javascript基础(非常详细)
哈喽!小伙伴们好久不见了,最近在忙些的事情更新的内容就比较少了。刚闲下来了就打算把之前断更的内容衔接上。以便爱学习的你能够及时关注,继续接下来的内容学习。好了话不多说就开始今天的内容分享吧。
之前我们讲过两篇关于web网页设计的一些相关基础知识,小伙伴们还有印象吗?要是没有的话小编这里也为大家准备了之前分享的内容链接。感兴趣的小伙伴点开链接看看,常说温故知新,才能更近一步。
以下是之前内容分享链接直接点击就可以自动跳转哦。
Web网页设计,HTML语言最全知识点总结(超级详解)图文并茂
Web前端网页设计CSS样式表语言从入门到精通(1),小白入门教程
以上就是之前分享的内容。
HTML-JavaScript基础篇:
什么是JavaScript
JavaScript是一种基于对象和事件驱动的、并具有安全性能的脚本语言,已经被广泛用于Web应用开发,常用来为网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果。通常JavaScript脚本是通过嵌入在HTML中来实现自身的功能的。
JavaScript特点
是一种解释性脚本语言(代码不进行预编译)。
主要用来向HTML(标准通用标记语言下的一个应用)页面添加交互行为。
可以直接嵌入HTML页面,但写成单独的js文件有利于结构和行为的分离。
跨平台特性,在绝大多数浏览器的支持下,可以在多种平台下运行(如Windows、Linux、Mac、Android、iOS等)。
JavaScript组成
一个完整的JavaScript实现应该由以下三个部分构成:
因此,了解以上三个内容,是我们能否学好弄通的基础哦。
JavaScript是解释型语言
JavaScript是一门解释型语言,所谓解释型值语言是指不需要被编译为机器码在执行,而是直接执行。由于少了编译这一步骤,所以解释型语言开发起来尤为轻松,但是解释型语言运行较慢也是它的劣势。不过解释型语言中使用了JIT技术,使得运行速度得以改善。
JavaScript是动态语言
JavaScript是一门动态语言,所谓的动态语言可以暂时理解为在语言中的一切内容都是不确定的。比如一个变量,这一时刻是个整型,下一时刻可能会变成字符串了。当然这个问题我们以后再谈。不过在补充一句动态语言相比静态语言性能上要差一些,不过由于JavaScript中应用的JIT技术,所以JavaScript可能是运行速度最快的动态语言了。
JavaScript是类似于 C 和 Java 的语法结构
JavaScript的语法结构与C和Java很像,向for、if、while等语句和Java的基本上是一模一样的。所以有过C和Java基础的同学学习起来会轻松很多。不过JavaScript和与Java的关系也仅仅是看起来像而已。
JavaScript是基于原型的面向对象
JavaScript是一门面向对象的语言,但是与Java不同JavaScript是基于原型的面向对象。
JavaScript是严格区分大小写
JavaScript是严格区分大小写的,也就是abc和Abc会被解析器认为是两个不同的东西。
JavaScript日常用途
- 嵌入动态文本于HTML页面。
- 对浏览器事件做出响应。
- 读写HTML元素。
- 该数据提交到服务器之前验证数据。
- 检测访客的浏览器信息。
- 控制cookies,包括创建和修改等。
- 基于Node.js技术进行服务器端编程。
JavaScript的基本结构元素:
<script type="text/javascript">
<!—
JavaScript 语句;
—>
</script >
--------------------------------------------------------
这串代码相比应该并不陌生吧,之前我们分享的前面几篇内容有个详细的介绍,这里就不过多赘述。
代码举例演示:
……
<title>初学JavaScript</title>
</head>
<body>
<script type="text/javascript">
document.write("初学JavaScript");
document.write("<h1>Hello,JavaScript</h1>");
</script>
</body>
</html>
-----------------------------------------------------------
以上就是一段标注格式的代码样式。小伙伴们可以手动输入以加深印象哦。
<script>…</script>可以包含在文档中的任何地方,只要保证这些代码在被使用前已读取并加载到内存即可
JavaScript的执行原理
网页中引用JavaScript的方式
标签引用在HTML中在script标签中就可以编写JavaScript代码,以下是一个简单演示。例如:
<script>
alert("Hello,World!");
</script>
文件引用在一个单独的js文件中也可以编写JavaScript代码,然后在HTML文件中使用script标签进行引用,以下是一个简单演示。
main.html 例如:
<script src="main.js"></script>
main.js 例如:
alert("Hello,World!");
页面输出如何使用JavaScript向页面输出一句话,请参考以下代码:
<script>
document.write("Hello,World!");
</script>
控制台输出如何使用JavaScript向控制台输出一句话,请参考以下代码。注意:页面按F12弹出控制台
<script>
console.log("输出一条日志");//最常用
console.info("输出一条信息");
console.warn("输出一条警告");
console.error("输出一条错误");
</script>
弹出窗口输出如何使用JavaScript向弹出窗口输出一句话,请参考以下代码
<script>
alert("Hello,World!");
</script>
JavaScript的注释注释中的内容不会被解析器解析执行,但是会在源码中显示,我们一般会使用注释对程序中的内容进行解释。
JS中的注释和Java的的一致,分为两种:
- 单行注释:// 注释内容
- 多行注释:/* 注释内容 */
<script>
// 这是注释内容
console.log("Hello,World!");
</script>
多行注释<script>
/**
* 这是注释内容
*/
console.log("Hello,World!");
</script>
JavaScript核心语法:
JavaScript基础语法:标识符
所谓标识符,就是指给变量、函数、属性或函数的参数起名字。
标识符可以是按照下列格式规则组合起来的一或多个字符:
第一个字符必须是一个字母、下划线( _ )或者一个美元符号( $ )。
其它字符可以是字母、下或者是是是是是是是是是是、美元符号或数字。
按照惯例,ECMAScript 标识符采用驼峰命名法。
标识符不能是关键字和保留字符。
常用关键字:
保留字符:
不建议使用的标识符:
单个单词的标识符举例:
name、age、gender、hobby
多个单词的标识符举例:
studentName、studentAge、studentGender、studentHobby
字面量和变量字面量字面量实际上就是一些固定的值,比如:1、2 、3、true、false、null、NaN、“hello”,字面量都是不可以改变的,由于字面量不是很方便使用,所以在JavaScript中很少直接使用字面量,使用的而是变量。
变量变量的作用是给某一个值或对象标注名称。比如我们的程序中有一个值123,这个值我们是需要反复使用的,这个时候 我们最好将123这个值赋值给一个变量,然后通过变量去使用123这个值。
变量的声明: 使用var关键字声明一个变量 例如:var a;
变量的赋值: 使用=为变量赋值。例如:a = 123;
声明和赋值同时进行: 例如:var a = 123;
数据类型类型分类数据类型决定了一个数据的特征,比如:123和”123”,直观上看这两个数据都是123,但实际上前者是一个数字,而后者是一个字符串。
对于不同的数据类型我们在进行操作时会有很大的不同。
JavaScript中一共有5种基本数据类型:
字符串型(String)
数值型(number)
布尔型(Boolean)
undefined型(Undefined)
null型(Null)
这5种之外的类型都称为Object,所以总的来看JavaScript中共有六种数据类型。
typeof运算符使用typeof操作符可以用来检查一个变量的数据类型。
使用方式:typeof 数据
示例代码:
console.log(typeof 123);
console.log(typeof "Hello,World!");
console.log(typeof true);
console.log(typeof Undefined);
console.log(typeof null)
StringString用于表示一个字符序列,即字符串。字符串需要使用 单引号 或 双引号 括起来。
转义字符:
注意:使用typeof运算符检查字符串时,会返回"string"
NumberNumber 类型用来表示整数和浮点数,最常用的功能就是用来表示10进制的整数和浮点数。
Number表示的数字大小是有限的,如果超过了这个范围,则会返回 ±Infinity。
最大值: 1.7976931348623157e 308
最小值:-1.7976931348623157e 308
0以上的最小值:5e-324
特殊的数字:
Infinity:正无穷
-Infinity:负无穷
NaN:非法数字(Not A Number)
其它的进制:
二进制:0b 开头表示二进制,但是,并不是所有的浏览器都支持
八进制:0 开头表示八进制
十六进制:0x 开头表示十六进制
注意:使用typeof检查一个Number类型的数据时(包括NaN 和 Infinity),会返回"number"。
Boolean布尔型也被称为逻辑值类型或者真假值类型。
布尔型只能够取真(true)和假(false)两种数值。除此以外, 其它的值都不被支持。
UndefinedUndefined 类型只有一个值,即特殊的 undefined。
在使用 var 声明变量但未对其加以初始化时,这个变量的值就是 undefined。
注意:使用typeof对没有初始化和没有声明的变量,会返回“undefined”。
NullNull 类型是第二个只有一个值的数据类型,这个特殊的值是 null。
undefined值实际上是由null值衍生出来的,所以如果比较undefined和null是否相等,会返回true。
注意:从语义上看null表示的是一个空的对象,所以使用typeof检查null会返回一个Object。
强制类型转换强制类型转换指将一个数据类型强制转换为其它的数据类型。一般是指,将其它的数据类型转换为String、Number、Boolean
将其它数值转换为字符串有三种方式:toString()、String()、 拼串。
方式一:调用被转换数据类型的toString()方法,该方法不会影响到原变量,它会将转换的结果返回,但是注意:null和undefined这两个值没有toString()方法,如果调用它们的方法,会报错。
例如:
var a = 123;
a = a.toString();
console.log(a);
console.log(typeof a);
方式二:调用String()函数,并将被转换的数据作为参数传递给函数,使用String()函数做强制类型转换时,对于Number和Boolean实际上就是调用的toString()方法,但是对于null和undefined,就不会调用toString()方法,它会将 null 直接转换为 “null”,将 undefined 直接转换为 “undefined”。
举例:
var a = 123;
a = String(a);
console.log(a);
console.log(typeof a);
var b = undefined;
b = String(b);
console.log(b);
console.log(typeof b);
var c = null;
c = String(c);
console.log(c);
console.log(typeof c);
方式三:为任意的数据类型 ""
举例:
var a = 123;
a = a "";
console.log(a);
console.log(typeof a);
转换为Number类型有三个函数可以把非数值转换为数值:Number()、parseInt() 和parseFloat()。Number()可以用来转换任意类型的数据,而后两者只能用于转换字符串。parseInt()只会将字符串转换为整数,而parseFloat()可以将字符串转换为浮点数。
方式一:使用Number()函数
字符串 --> 数字
如果是纯数字的字符串,则直接将其转换为数字
如果字符串中有非数字的内容,则转换为NaN
如果字符串是一个空串或者是一个全是空格的字符串,则转换为0
布尔 --> 数字
true 转成 1
false 转成 0
null --> 数字
null 转成 0
undefined --> 数字
undefined 转成 NaN
方式二:这种方式专门用来对付字符串,parseInt() 把一个字符串转换为一个整数
例如:
var a = "123";
a = parseInt(a);
console.log(a);
console.log(typeof a);
方式三:这种方式专门用来对付字符串,parseFloat() 把一个字符串转换为一个浮点数
举例:
var a = "123.456";
a = parseFloat(a);
console.log(a);
console.log(typeof a);
注意:如果对非String使用parseInt()或parseFloat(),它会先将其转换为String然后在操作
转换为Boolean类型将其它的数据类型转换为Boolean,只能使用Boolean()函数。
使用Boolean()函数
数字 —> 布尔
除了0和NaN,其余的都是true
字符串 —> 布尔
除了空串,其余的都是true
null和undefined都会转换为false
对象也会转换为true
运算符运算符也叫操作符,通过运算符可以对一个或多个值进行运算并获取运算结果。
比如:typeof就是运算符,可以来获得一个值的类型,它会将该值的类型以字符串的形式返回(number string boolean undefined object)
算术运算符算术运算符用于表达式计算。
y=5,下面的表格解释了这些算术运算符:
关系运算符
关系运算符在逻辑语句中使用,以测定变量或值是否相等。
x=5,下面的表格解释了比较运算符:
赋值运算符
赋值运算符用于给 JavaScript 变量赋值。
x=10 和 y=5,下面的表格解释了赋值运算符:
逻辑运算符
逻辑运算符用于测定变量或值之间的逻辑。
给定 x=6 以及 y=3,下表解释了逻辑运算符:
关于逻辑运算符我们可以具体探讨一下:
&& 如:&&可以对符号两侧的值进行运算并返回结果,运算规则如下:
两个值中只要有一个值为false,就返回false,只有两个值都为true时,才会返回true
JS中的“与”属于短路的与,如果第一个值为false,则不会检查第二个值
非布尔值时:如果两个都为true,则返回第二个值,如果两个值中有false,则返回靠前的false的值
|| 或:||可以对符号两侧的值进行运算并返回结果,运算规则如下:
两个值中只要有一个true,就返回true,只有两个值都为false,才会返回false
JS中的“或”属于短路的或,如果第一个值为true,则不会检查第二个值
非布尔值时:如果两个都为false ,则返回第二个值,如果两个值中有true,则返回靠前的true的值
! 非:!可以用来对一个值进行非运算,所谓非运算就是对一个布尔值进行取反操作,true变false,false变true,运算规则如下:
如果对一个值进行两次取反,它不会变化
非布尔值时:先会将其转换为布尔值,然后再取反,所以我们可以利用该特点,来将一个其它的数据类型转换为布尔值,可以为一个任意数据类型取两次反,来将其转换为布尔值,原理和Boolean()函数一样
比较运算符比较运算符用来比较两个值是否相等,如果相等会返回true,否则返回false。
使用 == 来做相等运算
当使用==来比较两个值时,如果值得类型不同,则会自动进行类型转换,将其转换为相同的类型,然后再比较
使用 != 来做不相等的运算
不相等用来判断两个值是否不相等,如果不相等返回true,否则返回false,不相等也会对变量进行自动的类型转换,如果转换后相等它也会返回false
使用 === 来做全等运算
用来判断两个值是否全等,它和相等类似,不同的是它不会做自动的类型转换,如果两个值得类型不同,直接返回false
使用 !== 来做全等运算
用来判断两个值是否不全等,它和不等类似,不同的是它不会做自动的类型转换,如果两个值得类型不同,直接返回true
条件运算符JavaScript 还包含了基于某些条件对变量进行赋值的运算符。
语法:variablename=(condition)?value1:value2;
举例:result=(age<18)?"年龄太小":"年龄合适";
执行流程:如果condition为true,则执行语句1,并返回执行结果,如果为false,则执行语句2,并返回执行结果。
逗号运算符使用逗号可以在一条语句中执行多次操作。
比如:var num1=1, num2=2, num3=3;
使用逗号运算符分隔的语句会从左到右顺 序依次执行。
运算符优先级运算符优先级由上到下依次减小,对于同级运算符,采用从左向右依次执行的方法。
代码块 语句
前边我所说的表达式和运算符等内容可以理解成是我们一 门语言中的单词,短语。而语句(statement)就是我们这个语言中一句一句完 整的话了。语句是一个程序的基本单位,JavaScript的程序就是由一条一条语句构成的,每一条语句使用;结尾。
JavaScript中的语句默认是由上至下顺序执行的,但是我们也可以通过一些流程控制语句来控制语句的执行顺序。
代码块代码块是在大括号 {} 中所写的语句,以此将多条语句的集合视为一条语句来使用。
例如:
{
var a = 123;
a ;
alert(a);
}
一般使用代码块将需要一起执行的语句进行分组,需要注意的是,代码块结尾不需要加 分号。
条件语句条件语句是通过判断指定表达式的值来决定执行还是跳过某些语句,最基本的条件语句:
if…else
- if…else
- switch…case
if…else语句是一种最基本的控制语句,它让JavaScript可以有条件的执行语句。
- 第一种形式:
if(expression)
statement
var age = 16;
if (age < 18) {
console.log("未成年");
}
第二种形式:
if(expression)
statement
else
statement
var age = 16;
if (age < 18) {
console.log("未成年");
} else {
console.log("已成年");
}
第三种形式:
if(expression1)
statement
else if(expression2)
statement
else
statement
var age = 18;
if (age < 18) {
console.log("小于18岁了");
} else if (age == 18) {
console.log("已经18岁了");
} else {
console.log("大于18岁了")
}
switch…caseswitch…case是另一种流程控制语句。
switch语句更适用于多条分支使用同一条语句的情况。
语法格式:
switch (语句) {
case 表达式1:
语句...
case 表达式2:
语句...
default:
语句...
}
注意:需要注意的是一旦符合case的条件程序会一直运行到结束,所以我们一般会在case中添加break作为语句的结束。
案例演示1:根据today的数值,输出今天是星期几。
var today = 1;
switch (today) {
case 1:
console.log("星期一");
break;
case 2:
console.log("星期二");
break;
case 3:
console.log("星期三");
break;
case 4:
console.log("星期四");
break;
case 5:
console.log("星期五");
break;
case 6:
console.log("星期六");
break;
case 7:
console.log("星期日");
break;
default:
console.log("输入错误");
}
循环语句循环语句和条件语句一样,也是基本的控制语句,只要满足一定的条件将会一直执行,最基本的循环语句:
while
- while
- do…while
- for
while语句是一个最基本的循环语句,while语句也被称为while循环。
语法格式:
while(条件表达式){
语句...
}
案例演示:输出1-10。
var i = 1;
while (i <= 10) {
console.log(i);
i ;
}
do…whiledo…while和while非常类似,只不过它会在循环的尾部而不是顶部检查表达式的值,因此,do…while循环会至少执行一次。相比于while,do…while的使用情况并不好 是很多。
语法格式:
do{
语句...
}while(条件表达式);
案例演示:输出1-10。
var i = 1;
do {
console.log(i);
i ;
} while (i <= 10);
forfor语句也是循环控制语句,我们也称它为for循环。大部分循环都会有一个计数器用以控制循环执行的次数, 计数器的三个关键操作是初始化、检测和更新。for语句 就将这三步操作明确为了语法的一部分。
语法格式:
for(初始化表达式 ; 条件表达式 ; 更新表达式){
语句...
}
案例演示:输出1-10。
for (var i = 1; i <= 10; i ) {
console.log(i);
}
跳转控制
- break:结束最近的一次循环,可以在循环和switch语句中使用。
- continue:结束本次循环,执行下一次循环,只能在循环中使用。
那如果我们想要跳出多层循环或者跳到指定位置该怎么办呢?可以为循环语句创建一个label,来标识当前的循环,如下例子:
outer: for (var i = 0; i < 10; i ) {
for (var j = 0; j < 10; j ) {
if (j == 5) {
break outer;
}
console.log(j);
}
}
对象基础概述
Object类型,我们也称为一个对象,是JavaScript中的引用数据类型。它是一种复合值,它将很多值聚合到一起,可以通过名字访问这些值。对象也可以看做是属性的无序集合,每个属性都是一个名/值对。对象除了可以创建自有属性,还可以通过从一个名为原型的对象那里继承属性。除了字符串、数字、true、false、null和undefined之外,JavaScript中的值都是对象。
创建对象创建对象有两种方式:
- 第一种方式:
var person = new Object();
person.name = "孙悟空";
person.age = 18;
console.log(person);
第二种方式:
var person = {
name: "孙悟空",
age: 18
};
console.log(person);
访问属性访问属性的两种方式:
- 第一种方式:使用 . 来访问
对象.属性名
第二种方式:使用 [] 来访问
对象[‘属性名’]
删除属性删除对象的属性可以使用delete关键字,格式如下:delete 对象.属性名
案例演示:
var person = new Object();
person.name = "孙悟空";
person.age = 18;
console.log(person);
delete person.name
console.log(person);
遍历对象枚举遍历对象中的属性,可以使用for … in语句循环,对象中有几个属性,循环体就会执行几次。
语法格式:
for (var 变量 in 对象) {
}
案例演示:
var person = {
name: "zhangsan",
age: 18
}
for (var personKey in person) {
var personVal = person[personKey];
console.log(personKey ":" personVal);
}
数据类型梳理基本数据类型JavaScript中的变量可能包含两种不同数据类型的值:基本数据类型和引用数据类型。
JavaScript中一共有5种基本数据类型:String、Number、 Boolean、Undefined、Null。
基本数据类型的值是无法修改的,是不可变的。
基本数据类型的比较是值得比较,也就是只要两个变量的值相等,我们就认为这两个变量相等
引用数据类型引用类型的值是保存在内存中的对象。
当一个变量是一个对象时,实际上变量中保存的并不是对象本身,而是对象的引用。
当从一个变量向另一个变量复制引用类型的值时,会将对象的引用复制到变量中,并不是创建一个新的对象。
这时,两个变量指向的是同一个对象。因此,改变其中一个变量会影响另一个。
栈和堆梳理JavaScript在运行时数据是保存到栈内存和堆内存当中的。
简单来说栈内存用来保存变量和基本类型,堆内存是用来保存对象。
我们在声明一个变量时,实际上就是在栈内存中创建了一个空间用来保存变量。
如果是基本类型则在栈内存中直接保存,如果是引用类型则会在堆内存中保存,变量中保存的实际上对象在堆内存中的地址。
当我们写了下边这几句代码的时候,栈内存和堆内存的结构如下:
var a = 123;
var b = true;
var c = "hello";
var d = {name: 'sunwukong', age: 18};
栈的特点:先进后出,后进先出
函数
概述
函数是由一连串的子程序(语句的集合)所组成的,可以被外部程序调用,向函数传递参数之后,函数可以返回一定的值。
通常情况下,JavaScript代码是自上而下执行的,不过函数体内部的代码则不是这样。如果只是对函数进行了声明,其中的代码并不会执行,只有在调用函数时才会执行函数体内部的代码。
这里要注意的是JavaScript中的函数也是一个对象,使用typeof检查一个函数对象时,会返回function。
函数创建
- 使用 函数对象 来创建一个函数(几乎不用)
- 语法格式:
- var 函数名 = new Function("执行语句");
- 示例代码:
- var fun = new Function("console.log('这是我的第一个函数');");
使用 函数声明 来创建一个函数(比较常用)
语法格式:function 函数名([形参1,形参2,...,形参N]) {
语句...
}
示例代码:function fun(){
console.log("这是我的第二个函数");
}
使用 函数表达式 来创建一个函数(比较常用)
语法格式:var 函数名 = function([形参1,形参2,...,形参N]) {
语句....
}
示例代码:var fun = function() {
console.log("这是我的第三个函数");
}
函数调用
- 对于无参函数调用
// 函数声明
var fun = function () {
console.log("哈哈,我执行啦!");
}
// 函数调用
fun();
对于有参考函数调用:
// 函数声明
var sum = function (num1, num2) {
var result = num1 num2;
console.log("num1 num2 = " result);
}
// 函数调用
sum(10, 20);
函数参数JS中的所有的参数传递都是按值传递的,也就是说把函数外部的值赋值给函数内部的参数,就和把值从一个变量赋值给另一个变量是一样的,在调用函数时,可以在()中指定实参(实际参数),实参将会赋值给函数中对应的形参
调用函数时,解析器不会检查实参的类型,所以要注意,是否有可能会接收到非法的参数,如果有可能,则需要对参数进行类型的检查,函数的实参可以是任意的数据类型
调用函数时,解析器也不会检查实参的数量,多余实参不会被赋值,如果实参的数量少于形参的数量,则没有对应实参的形参将是undefined
函数返回值可以使用 return 来设置函数的返回值,return后的值将会作为函数的执行结果返回,可以定义一个变量,来接收该结果。
注意:在函数中return后的语句都不会执行,如果return语句后不跟任何值就相当于返回一个undefined,如果函数中不写return,则也会返回undefined,return后可以跟任意类型的值
语法格式:return 值
案例演示:
function sum(num1, num2) {
return num1 num2;
}
var result = sum(10, 20);
console.log(result);
嵌套函数嵌套函数:在函数中声明的函数就是嵌套函数,嵌套函数只能在当前函数中可以访问,在当前函数外无法访问。
案例演示:function fu() {
function zi() {
console.log("我是儿子")
}
zi();
}
fu();
匿名函数匿名函数:没有名字的函数就是匿名函数,它可以让一个变量来接收,也就是用 “函数表达式” 方式创建和接收。
案例演示:var fun = function () {
alert("我是一个匿名函数");
}
fun();
立即执行函数立即执行函数:函数定义完,立即被调用,这种函数叫做立即执行函数,立即执行函数往往只会执行一次。
案例演示:(function () {
alert("我是一个匿名函数");
})();
对象中的函数对象的属性值可以是任何的数据类型,也可以是个函数。
如果一个函数作为一个对象的属性保存,那么我们称这个函数是这个对象的方法,调用这个函数就说调用对象的方法(method)。
注意:方法和函数只是名称上的区别,没有其它别的区别
案例演示:var person = {
name: "zhangsan",
age: 18,
sayHello: function () {
console.log(name " hello")
}
}
person.sayHello();
this对象
解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this,this指向的是一个对象,这个对象我们称为函数执行的上下文对象,根据函数的调用方式的不同,this会指向不同的对象
以函数的形式调用时,this永远都是window
以方法的形式调用时,this就是调用方法的那个对象
案例演示:
//创建一个全局变量name
var name = "全局变量name";
//创建一个函数
function fun() {
console.log(this.name);
}
//创建一个对象
var obj = {
name: "孙悟空",
sayName: fun
};
//我们希望调用obj.sayName()时可以输出obj的名字而不是全局变量name的名字
obj.sayName();
对象进阶用工厂方法创建对象我们之前已经学习了如何创建一个对象,那我们要是想要创建多个对象又该怎么办?聪明的同学可能会说,直接再写几个对象不就好了吗?比如下边的代码:
var person1 = {
name: "孙悟空",
age: 18,
sayName: function () {
console.log(this.name);
}
};
var person2 = {
name: "猪八戒",
age: 19,
sayName: function () {
console.log(this.name);
}
};
var person3 = {
name: "沙和尚",
age: 20,
sayName: function () {
console.log(this.name);
}
};
console.log(person1);
console.log(person2);
console.log(person3);
上述代码确实可以创建多个对象,但是,这样的解决方案真的好吗?对于少量对象可能使用,我们假设说,要用循环创建1000个对象,那你这种办法似乎就没用了,我们可以这么做,如下代码:// 使用工厂模式创建对象
function createPerson() {
// 创建新的对象
var obj = new Object();
// 设置对象属性
obj.name = "孙悟空";
obj.age = 18;
// 设置对象方法
obj.sayName = function () {
console.log(this.name);
};
//返回新的对象
return obj;
}
var person1 = createPerson();
var person2 = createPerson();
var person3 = createPerson();
console.log(person1);
console.log(person2);
console.log(person3);
上述代码看起来更加简洁,但是你会发现每一个人都是孙悟空,我们要是想要给每一个人不同的属性值,请参考:
// 使用工厂模式创建对象
function createPerson(name, age) {
// 创建新的对象
var obj = new Object();
// 设置对象属性
obj.name = name;
obj.age = age;
// 设置对象方法
obj.sayName = function () {
console.log(this.name);
};
//返回新的对象
return obj;
}
var person1 = createPerson("孙悟空", 18);
var person2 = createPerson("猪八戒", 19);
var person3 = createPerson("沙和尚", 20);
console.log(person1);
console.log(person2);
console.log(person3);
现在再看上述代码,发现好像已经完美的解决了创建多个对象的难题,那我们是不是可以用循环批量创建1000个对象了呢?那我们就来试试:// 使用工厂模式创建对象
function createPerson(name, age) {
// 创建新的对象
var obj = new Object();
// 设置对象属性
obj.name = name;
obj.age = age;
// 设置对象方法
obj.sayName = function () {
console.log(this.name);
};
//返回新的对象
return obj;
}
for (var i = 1; i <= 1000; i ) {
var person = createPerson("person" i, 18);
console.log(person);
}
这样我们就实现了批量创建对象的功能,至于对象的名称和年龄,我们可以通过名称数组和年龄数组来获取,但这并不是我们本小节的重点,我们就忽略了。
用构造函数创建对象在前一节中,我们学会了使用工厂模式创建对象,但是,你会发现我们所创建的对象类型都是Object,具体代码如下:
// 使用工厂模式创建对象
function createPerson(name, age) {
// 创建新的对象
var obj = new Object();
// 设置对象属性
obj.name = name;
obj.age = age;
// 设置对象方法
obj.sayName = function () {
console.log(this.name);
};
//返回新的对象
return obj;
}
for (var i = 1; i <= 1000; i ) {
var person = createPerson("person" i, 18);
console.log(typeof person);
}
那这构造函数到底是什么呢?我来解释一下:
构造函数:构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写,构造函数和普通函数的还有一个区别就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用。
那构造函数是怎么执行创建对象的过程呢?我再来解释一下:
调用构造函数,它会立刻创建一个新的对象
将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象
逐行执行函数中的代码
将新建的对象作为返回值返回
你会发现构造函数有点类似工厂方法,但是它创建对象和返回对象都给我们隐藏了,使用同一个构造
函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类。我们将通过一个构造函数创建的对象,称为是该类的实例。
现在,this又出现了一种新的情况,为了不让大家混淆,我再来梳理一下:
当以函数的形式调用时,this是window
当以方法的形式调用时,谁调用方法this就是谁
当以构造函数的形式调用时,this就是新创建的那个对象
我们可以使用 instanceof 运算符检查一个对象是否是一个类的实例,它返回true或false
语法格式:对象 instanceof 构造函数
案例演示:console.log(person1 instanceof Person);
原型在前一节中,我们学习了使用构造函数的方式进行创建对象,但是,它还是存在一个问题,那就是,你会发现,每一个对象的属性不一样这是一定的,但是它的方法似乎好像是一样的,如果我创建1000个对象,那岂不是内存中就有1000个相同的方法,那要是有10000个,那对内存的浪费可不是一点半点的,我们有没有什么好的办法解决,没错,我们可以把函数抽取出来,作为全局函数,在构造函数中直接引用就可以了,上代码:
// 使用构造函数来创建对象
function Person(name, age) {
// 设置对象的属性
this.name = name;
this.age = age;
// 设置对象的方法
this.sayName = sayName
}
// 抽取方法为全局函数
function sayName() {
console.log(this.name);
}
var person1 = new Person("孙悟空", 18);
var person2 = new Person("猪八戒", 19);
var person3 = new Person("沙和尚", 20);
person1.sayName();
person2.sayName();
person3.sayName();
在全局作用域中定义函数却不是一个好的办法,为什么呢?因为,如果要是涉及到多人协作开发一个项目,别人也有可能叫sayName这个方法,这样在工程合并的时候就会导致一系列的问题,污染全局作用域,那该怎么办呢?有没有一种方法,我只在Person这个类的全局对象中添加一个函数,然后在类中引用?答案肯定是有的,这就需要原型对象了,我们先看看怎么做的,然后在详细讲解原型对象。
// 使用构造函数来创建对象
function Person(name, age) {
// 设置对象的属性
this.name = name;
this.age = age;
}
// 在Person类的原型对象中添加方法
Person.prototype.sayName = function() {
console.log(this.name);
};
var person1 = new Person("孙悟空", 18);
var person2 = new Person("猪八戒", 19);
var person3 = new Person("沙和尚", 20);
person1.sayName();
person2.sayName();
person3.sayName();
那原型(prototype)到底是什么呢?
我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象,即显式原型,原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。
如果函数作为普通函数调用prototype没有任何作用,当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过__proto__(隐式原型)来访问该属性。当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。
以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样就不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了。
原型链访问一个对象的属性时,先在自身属性中查找,找到返回, 如果没有,再沿着__proto__这条链向上查找,找到返回,如果最终没找到,返回undefined,这就是原型链,又称隐式原型链,它的作用就是查找对象的属性(方法)。
我们使用一张图来梳理一下上一节原型案例的代码:
注意:Object对象是所有对象的祖宗,Object的原型对象指向为null,也就是没有原型对象
toString()函数用于将当前对象以字符串的形式返回。该方法属于Object对象,由于所有的对象都"继承"了Object的对象实例,因此几乎所有的实例对象都可以使用该方法,所有主流浏览器均支持该函数。
// 使用构造函数来创建对象
function Person(name, age) {
// 设置对象的属性
this.name = name;
this.age = age;
}
//创建对象的一个实例对象
var p = new Person("张三", 20);
console.log(p.toString());
JavaScript的许多内置对象都重写了该函数,以实现更适合自身的功能需要。
注意:这里我们只是演示toString()方法,其它的一些没有讲到的知识后边会将,我们只看效果就可。
// 字符串
var str = "Hello";
console.log(str.toString());
// 数字
var num = 15.26540;
console.log(num.toString());
// 布尔
var bool = true;
console.log(bool.toString());
// Object
var obj = {name: "张三", age: 18};
console.log(obj.toString());
// 数组
var array = ["CodePlayer", true, 12, -5];
console.log(array.toString());
// 日期
var date = new Date(2013, 7, 18, 23, 11, 59, 230);
console.log(date.toString());
// 错误
var error = new Error("自定义错误信息");
console.log(error.toString());
// 函数
console.log(Function.toString());
hasOwnProperty方法前边章节我们学过,如何遍历一个对象所有的属性和值,那我们要是判断当前对象是否包含指定的属性或方法可以使用 in 运算符来检查,如下代码演示:
// 创造一个构造函数
function MyClass() {
}
// 向MyClass的原型中添加一个name属性
MyClass.prototype.name = "我是原型中的名字";
// 创建一个MyClass的实例
var mc = new MyClass();
mc.age = 18;
// 使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
console.log("age" in mc);
console.log("name" in mc);
我只想要检查自身对象是否含有某个方法或属性,我们可以使用Object的hasOwnProperty()方法,它返回一个布尔值,判断对象是否包含特定的自身(非继承)属性。如下代码演示
// 创造一个构造函数
function MyClass() {
}
// 向MyClass的原型中添加一个name属性
MyClass.prototype.name = "我是原型中的名字";
// 创建一个MyClass的实例
var mc = new MyClass();
mc.age = 18;
// 使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
console.log("age" in mc);
console.log("name" in mc);
// 可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性,使用该方法只有当对象自身中含有属性时,才会返回true
console.log(mc.hasOwnProperty("age"));
console.log(mc.hasOwnProperty("name"));
同学可能会有疑问,我的这个MyClass类对象中没有hasOwnProperty这个方法啊,它是哪来的?对了,就是原型中的,在执行方法的时候它会通过原型链进行查找,这个方法是Object的特有方法,如下代码演示:
// 创造一个构造函数
function MyClass() {
}
// 向MyClass的原型中添加一个name属性
MyClass.prototype.name = "我是原型中的名字";
// 创建一个MyClass的实例
var mc = new MyClass();
mc.age = 18;
// 检查当前对象
console.log(mc.hasOwnProperty("hasOwnProperty"));
// 检查当前对象的原型对象
console.log(mc.__proto__.hasOwnProperty("hasOwnProperty"));
// 检查当前对象的原型对象的原型对象
console.log(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty"));
对象继承
前边我们一直在说继承,那什么是继承?它有什么作用?如何实现继承?将会是本章节探讨的问题。
面向对象的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。但是在JavaScript中没有类的概念,前边我们说所的类只是我们自己这么叫,大家要清楚。因此它的对象也与基于类的对象有所不同。实际上,JavaScript语言是通过一种叫做原型(prototype)的方式来实现面向对象编程的。
那实现继承有一个最大的好处就是子对象可以使用父对象的属性和方法,从而简化了一些代码。
JavaScript有六种非常经典的对象继承方式,但是我们只学习前三种:
原型链继承
- 原型链继承
- 借用构造函数继承
- 组合继承(重要)
- 原型式继承
- 寄生式继承
- 寄生组合式继承人
核心思想: 子类型的原型为父类型的一个实例对象
基本做法:
- 定义父类型构造函数
- 给父类型的原型添加方法
- 定义子类型的构造函数
- 创建父类型的对象赋值给子类型的原型
- 将子类型原型的构造属性设置为子类型
- 给子类型原型添加方法
- 创建子类型的对象: 可以调用父类型的方法
案例演示:
// 定义父类型构造函数
function SupperType() {
this.supProp = 'Supper property';
}
// 给父类型的原型添加方法
SupperType.prototype.showSupperProp = function () {
console.log(this.supProp);
};
// 定义为子类型的构造函数
function SubType() {
this.subProp = 'Sub property';
}
// 创建父类型的对象赋值给子类型的原型
SubType.prototype = new SupperType();
// 将子类型原型的构造属性设置为子类型
SubType.prototype.constructor = SubType;
// 给自己类型原型添加方法
SubType.prototype.showSubProp = function () {
console.log(this.subProp)
};
// 创建子类型的对象: 可以调用父类型的方法
var subType = new SubType();
subType.showSupperProp();
subType.showSubProp();
缺点描述:
借用构造函数继承
- 原型链继承多个实例的引用类型属性指向相同,一个实例修改了原型属性,另一个实例的原型属性也会被修改
- 不能传递参数
- 继承单一
核心思想: 使用.call()和.apply()将父类构造函数引入子类函数,使用父类的构造函数来增强子类实例,等同于复制父类的实例给子类
基本做法:
定义父类型构造函数
定义为子类型的构造函数
给自己类型的原型添加方法
创建子类型的对象然后调用
案例演示:
借用构造函数继承的重点就在于SuperType**.call(this, name)**,调用了SuperType构造函数,这样,
SubType的每个实例都会将SuperType中的属性复制一份。
// 定义父类型构造函数
function SuperType(name) {
this.name = name;
this.showSupperName = function () {
console.log(this.name);
};
}
// 定义为子类型的构造函数
function SubType(name, age) {
// 在子类型中调用call方法继承自SuperType
SuperType.call(this, name);
this.age = age;
}
// 给自己类型的原型添加方法
SubType.prototype.showSubName = function () {
console.log(this.name);
};
// 创建子类型的对象然后调用
var subType = new SubType("孙悟空", 20);
subType.showSupperName();
subType.showSubName();
console.log(subType.name);
console.log(subType.age);
缺点描述:
组合继承
- 只能继承父类的实例属性和方法,不能继承原型属性和方法
- 无法实现构造函数的复用,每个子类都有父类实例函数的副本,影响性能,代码会臃肿
核心思想: 原型链 借用构造函数的组合继承
基本做法:
- 利用原型链实现对父类型对象的方法继承
- 利用super()借用父类型构建函数初始化相同属性
案例演示:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.setName = function (name) {
this.name = name;
};
function Student(name, age, price) {
Person.call(this, name, age); // 为了得到父类型的实例属性和方法
this.price = price; // 添加子类型私有的属性
}
Student.prototype = new Person(); // 为了得到父类型的原型属性和方法
Student.prototype.constructor = Student; // 修正constructor属性指向
Student.prototype.setPrice = function (price) { // 添加子类型私有的方法
this.price = price;
};
var s = new Student("孙悟空", 24, 15000);
console.log(s.name, s.age, s.price);
s.setName("猪八戒");
s.setPrice(16000);
console.log(s.name, s.age, s.price);
缺点描述:
- 父类中的实例属性和方法既存在于子类的实例中,又存在于子类的原型中,不过仅是内存占用,因此,在使用子类创建实例对象时,其原型中会存在两份相同的属性和方法 。
注意:这个方法是JavaScript中最常用的继承模式
垃圾回收
垃圾回收(GC):就像人生活的时间长了会产生垃圾一样,程序运行过程中也会产生垃圾,这些垃圾积攒过多以后,会导致程序运行的速度过慢,所以我们需要一个垃圾回收的机制,来处理程序运行过程中产生垃圾。
当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操作该对象,此时这种对象就是一个垃圾,这种对象过多会占用大量的内存空间,导致程序运行变慢,所以这种垃圾必须进行清理。
在JS中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,我们不需要也不能进行垃圾回收的操作,我们需要做的只是要将不再使用的对象设置null即可。
案例演示:
// 使用构造函数来创建对象
function Person(name, age) {
// 设置对象的属性
this.name = name;
this.age = age;
}
var person1 = new Person("孙悟空", 18);
var person2 = new Person("猪八戒", 19);
var person3 = new Person("沙和尚", 20);
person1 = null;
person2 = null;
person3 = null;
作用域作用域指一个变量的作用的范围,在JS中一共有两种作用域:
- 全局作用域
- 函数作用域
声明提前
变量的声明提前:使用var关键字声明的变量,会在所有的代码执行之前被声明(但是不会赋值),但是如果声明变量时不使用var关键字,则变量不会被声明提前
函数的声明提前:使用函数声明形式创建的函数 function 函数名(){} ,它会在所有的代码执行之前就被创建,所以我们可以在函数声明前来调用函数。使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用
作用域全局作用域直接编写在script标签中的JavaScript代码,都在全局作用域
全局作用域在页面打开时创建,在页面关闭时销毁
在全局作用域中有一个全局对象window,它代表的是一个浏览器的窗口,它由浏览器创建,我们可以直接使用
在全局作用域中:
创建的变量都会作为window对象的属性保存
创建的函数都会作为window对象的方法保存
全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到
好了今天的内容分享就到这里感谢你的持续关注,我们下期再会!,
免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com