作用域内置对象和数据类型
作用域
作用域:代码变量在某个范围内起作用和效果。目标是提高可靠性,并减少命名冲突。
1.全局作用域:整个script标签都是一个单独的js文件
局部作用域就是指在函数内部是起作用的。
2.全局变量,有两种是全局变量:1全局作用域下声明的变量为全局的变量,2或者在函数内部没声明直接赋值的变量。
js es6 之前没有块级作用域,
只有全局与局部(函数作用域)两种作用域。
在es6才新增块级作用域。
块级作用域
{} 有花括号包含的部分就叫做块级作用域。
注意!有{}的不一定是函数,也可能是,if 或者for。
下面的这个例子就不是写的函数的例子。
if(true){
var num = 123;
console.log(num); //123
}
console.log(num); //不会报错
如果是java代码
if(true){
int num = 123;
system.out.print(num); // 123
}
system.out.print(num); //报错
变量作用域
作用域链,链式查找,就近原则
1 .特殊情况下,在函数内不声明即使用的变量,是全局变量(不建议使用)
2 .局部变量只能在该函数内部使用
局部变量只能在该函数内部使用
js在es6前没有块级作用域
那么一个函数内部的变量就可以被外部调用,这和前面局部变量只能在该函数内使用时相互冲突的,这是怎么回事?
解答:函数内部的局部变量不能用在外部调用,只能在函数内部使用。es6之前没有的是块级作用域,块级作用域中定义的变量可以在块级以外使用,块级作用域不是函数作用域,两码事,所以不冲突。
js引擎预解析
js运行两个步骤:
1.预解析
2.运行
预解析会把代码中所有var和function提升到当前作用域最前面。
作用域分为全局作用域和局部作用域。
预解析分为变量预解析和函数预解析
1 .变量预解析(变量提升):只做声明不做赋值。
2 .函数表达式声明函数
var fun = function () {
console.log('print');
}
函数表达式形式定义的函数,调用必须写在函数下面,不然报错。
例:
fn();
var fn = function() {
console.log('想不到吧');
}
执行步骤:
1.隐函数定义式中的变量fn 先提升到代码最前端,此时fn未定义,为undefined
2.然后fn();调用了fn 所以报错。
案例:
var a = b = c = 9;
相当于var a = 9; c = 9; b = 9; b c没有声明就已经赋值,所以b,c是全局变量。
如果要实现abc 一次赋值并属于局部变量,需要写为:
var a = 0,b = 0,c = 0;
对象
万物皆对象,但必须具象,具有唯一性。不能是某些合集或者不确定的,比如老师,女朋友。
复杂数据类型 Object(对象)
ECMAScript中的对象其实就是一组数据(属性)和功能(方法)的集合。
属性:事物的特征,在对象中用属性来表示(名词)
方法:事物的行为,在对象中用方法来表示(动词)
用对象的表示方法可以结构清晰更强大,
为了让更好地存储一组数据,对象应运而生。
对象中为每项数据设置了属性名称,可以访问数据更语义化,数据结构清晰,表意明显,方便开发者使用。
多个属性或者方法中间用逗号隔开!!!
三种办法创建对象:
1.利用字面量创建
2.利用new object创建
3.利用构造函数创建
1.字面量创建
var obj = {
username: "charlotte",
age:18,
sex:female,
sayHi: function() {
console.log('hi~');
}
}
花括号内包含属性与方法 采用键值对的方式存储
键对应属性名,值对应属性值;
包括函数也是这样声明,名字:函数
调用对象属性:
方法1
对象名.属性名
console.log(obj.name);
方法2
对象名[‘属性名’]
console.log(obj[‘age’]);
调用对象方法:
对象名.方法名() //注意 多加了括号。
obj.sayHi();
2.newObject创建对象
var obj = new Object();
obj.uname = 'nancy';
obj.sex = female;
obj.sayHi = function() {
console.log('Hi~');
}
3.构造函数方式创建对象
构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。前面两种办法一次创建一个对象。
1.找到相同属性封装进去
function 构造函数名(形参1,形参2,形参三) {
this.属性1 = 形参1; //注意这里是分号;
this.属性2 = 形参2; //注意这里是分号;
this.属性3 = 形参3; //注意这里是分号;
this.方法 = function() {
}
}
调用:
var obj = new 构造函数名(实参1,实参2,实参3)
例:
function Star(uname, sex, age) {
this.name = uname;
this.sex = sex;
this.age = age;
}
var tomCruise = new Star('tom', 'male', 58);
console.log(tomCruise.name);
构造函数不需要return就可以返回结果。
构造函数首字母大写,注意是构造函数的那个首字母大写
new 关键字的作用:
1 .在内存创建新的空对象
2 .this指向这个新对象
3 .执行构造函数里面的代码给新对象添加属性方法
4 .返回这个新对象
for in 遍历对象
for…in 语句用于遍历数组或者对象的属性(对数组或者对象的属性进行循环操作)。
for var k in
k不是固定的,但是我们习惯性写k。
for (var k in obj) {
console.log(k);//k变量,输出得到的是属性的名字不是值。
console.log(obj[k]);//obj[k] 得到 属性里面的值。
}
var arytest = ['red', 'green', 'blue', 'pink'];
for (var k in arytest){
console.log(k);
console.log(arytest[k]);
}
//打印结果
0
red
1
green
2
blue
3
pink
对象是复杂数据类型object
本质是一组无序的相关属性与方法的集合
构造函数泛指一个大类
对象实指一个
内置对象
JS中对象分三类:
1 .自定义对象
2 .内置对象
3 .浏览器对象
前面两种是JS基础内容,属于ECMAscript标准规定。JS自带内置对象,供开发者使用提供常见或必要功能,内置对象最大优点就是帮助我们快速开发。第三个浏览器对象属于js独有。
提供了多个内置对象:Math,Date,Array,String等
1.math对象
math 是一个对象。所有内容都是静态的。直接使用。
Math.round四舍五入,其他数字都是四舍五入,但是.5这个数字是往整体数字大了取。
小数的情况下要多考虑。
属性、方法名 | 功能 |
---|---|
Math.PI | 圆周率 |
Math.floor() | 向下取整,往小了取 |
Math.ceil() | 向上取整,往大了取 |
Math.round() | 四舍五入版 就近取整 注意 -3.5 结果是 -3 |
Math.abs() | 绝对值 |
Math.max()/Math.min() | 求最大和最小值 |
Math.random() | 获取范围在[0,1)内的随机值 |
由Math.random 得到任意区间的随机整数(返回 min(包含)~ max(包含)之间的数字)
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
2.Date日期对象
Date是一个构造函数(看不懂的话回去看 ‘构造函数方式创建对象’),所以必须配合别的具体对象才能使用,即需要实例化之后才能用其中的具体方法和属性。
- 自动获取系统当前时间
var now = new Date();//Date()内没有参数返回系统时间
console.log(now);
- 获取指定时间
var future = new Date('2019/5/1');
var future =new Date('2019-5-2');
- Date构造函数的方法与说明:
注意:获取的月份是 0 - 11,所以要在获取的月份上 +1 才是正确时间。
- 格式化日期年月日输出
//按照自己的格式输出时间
//先自定义一个date
var date = new Date();//Date()内没有参数,则返回系统时间
console.log(date);
//注意 date小写,getFullYear 驼峰命名
var year = date.getFullYear();
var month = date.getMonth() + 1;
var dates = date.getDate();
var xingqi = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
var day = date.getDay();
console.log('今天是' + year + '年' + month + '月' + dates + '日' + xingqi[day]);
-
格式化日期 时分秒的输出
//格式化时分秒的输出 function getTime() { var time = new Date(); var h = time.getHours(); h = h < 10 ? '0' + h : h; var m = time.getMinutes(); m = m < 10 ? '0' + m : m; var s = time.getSeconds(); s = s < 10 ? '0' + s : s; return h + ':' + m + ':' + s; } console.log(getTime());
-
根据不同时间做出不同反应:
// 根据系统不同时间来判断,所以需要用到日期内置对象
// 利用多分支语句来设置不同的图片
// 需要一个图片,并且根据时间修改图片,就需要用到操作元素src属性
// 需要一个div元素,显示不同问候语,修改元素内容即可
// 1.获取元素
var img = document.querySelector('img');
var div = document.querySelector('div');
// 2. 得到当前的小时数
var date = new Date(); //由date构造函数构造出来新的对象 内部包含所有方法
var h = date.getHours(); // 从date中通过方法 gethour 取出所需要的数据,赋值给h
// 3. 判断小时数改变图片和文字信息
if (h < 12) {
img.src = 'images/s.gif';
div.innerHTML = '亲,上午好,好好写代码';
} else if (h < 18) {
img.src = 'images/x.gif';
div.innerHTML = '亲,下午好,好好写代码';
} else {
img.src = 'images/w.gif';
div.innerHTML = '亲,晚上好,好好写代码';
}
-
通过Date获取时间戳
1 .getTime() 返回 1970 年 1 月 1 日至今的毫秒数。
2 .valueOf() 一样的效果。
3 .var dat1 = +new Date();
4 .console.log(Date.now());
案例:倒计时效果
// 倒计时效果
// 1.核心算法:输入的时间减去现在的时间就是剩余的时间,即倒计时 ,但是不能拿着时分秒相减,比如 05 分减去25分,结果会是负数的。
// 2.用时间戳来做。用户输入时间总的毫秒数减去现在时间的总的毫秒数,得到的就是剩余时间的毫秒数。
// 3.把剩余时间总的毫秒数转换为天、时、分、秒 (时间戳转换为时分秒)
// 转换公式如下:
// d = parseInt(总秒数/ 60/60 /24); // 计算天数
// h = parseInt(总秒数/ 60/60 %24) // 计算小时
// m = parseInt(总秒数 /60 %60 ); // 计算分数
// s = parseInt(总秒数%60); // 计算当前秒数
function countDown(time) {
var nowTime = +new Date(); // 返回的是当前时间总的毫秒数
var inputTime = +new Date(time); // 返回的是用户输入时间总的毫秒数
var times = (inputTime - nowTime) / 1000; // times是剩余时间总的秒数
var d = parseInt(times / 60 / 60 / 24); // 天
d = d < 10 ? '0' + d : d;
var h = parseInt(times / 60 / 60 % 24); //时
h = h < 10 ? '0' + h : h;
var m = parseInt(times / 60 % 60); // 分
m = m < 10 ? '0' + m : m;
var s = parseInt(times % 60); // 当前的秒
s = s < 10 ? '0' + s : s;
return d + '天' + h + '时' + m + '分' + s + '秒';
}
console.log(countDown('2021-5-1 18:00:00'));
var date = new Date();
console.log(date);
3.数组对象
3.1检测是否为数组的两种方法
-
instanceof 运算符
instance v. 举例 n.例子
-
instanceof 可以判断一个对象是否是某个构造函数的实例
var arr = [1, 23]; var obj = {}; console.log(arr instanceof Array); // true console.log(obj instanceof Array); // false
-
-
Array.isArray()
-
Array.isArray()用于判断一个对象是否为数组,isArray() 是 HTML5 中提供的方法
var arr = [1, 23]; var obj = {}; console.log(Array.isArray(arr)); // true console.log(Array.isArray(obj)); // false
h5新增方法 ie9以上才支持
-
3.2添加、删除数组元素方法
1 .push(参数1):在数组末尾添加一个或者多个元素
push完毕之后返回新数组的长度
var arr = [1,2,3];
console.log(arr.push(4,'nihaoma'));
2 .unshift ( )
在数组前面添加元素,完毕后返回数组新长度。
console.log(arr.unshift(‘red’,223));
3.pop()删除数组最后一个元素
一次之能删除一个,不跟参数。
返回值为被删除的那个元素。
4.shift()
删除数组第一个元素。
var arr = new Array(4)
arr[0] = "合肥"
arr[1] = "上海"
arr[2] = "广州"
arr[3] = "天津"
document.write("原有数组元素为:" + arr)
document.write("<br />")
document.write("删除数组中的第一个元素为:" + arr.pop())
document.write("<br />")
document.write("删除元素后的数组为:" + arr)
3.3数组排序
1.reverse()反转数组
var arr = new Array(4)
arr[0] = "合肥"
arr[1] = "上海"
arr[2] = "广州"
arr[3] = "天津"
document.write("原有数组元素为:" + arr)
document.write("<br />")
document.write("反转数组:" + arr.reverse())
document.write("<br />")
document.write("反转元素后的数组为:" + arr)
2.sort()数组排序
join() 方法用于把数组中的所有元素放入一个字符串。
var arrSimple=new Array(1,8,7,6,2,5);
arrSimple.sort();
// document.writeln(arrSimple.join());
console.log(arrSimple.join())
语法:arrayObject.sort(sortby);
参数sortby 可选,用来规定排序的顺序,但必须是函数。
如果要得到自己想要的结果,不管是升序还是降序,就需要提供比较函数了。该函数比较两个值的大小,然后返回一个用于说明这两个值的相对顺序的数字。
比较函数应该具有两个参数 a 和 b,其返回值如下:
若 a 小于 b,即 a - b 小于零,则返回一个小于零的值,数组将按照升序排列。
若 a 等于 b,则返回 0。
若 a 大于 b, 即 b - a 结果小于零,则返回一个小于零的值,数组将按照降序排列。
var arr = [7, 3, 6, 1, 5, 12];
console.log(arr.sort());//[1, 12, 3, 5, 6, 7]
console.log(arr.sort(function (a, b) {
return a - b;
})); //[1, 3, 5, 6, 7, 12]
3.4数组索引方法
indexof
1 .找到返回在数组的索引号
2 .找不到就返回-1。
3 .如果有重复,只返回第一个找到的数组索引号
array.indexOf(item,start)
参数 | 描述 |
---|---|
item | 必须。查找的元素。 |
start | 可选的整数参数。规定在数组中开始检索的位置。它的合法取值是 0 到 stringObject.length - 1。如省略该参数,则将从字符串的首字符开始检索。 |
start 从0开始计算。数组内容数字位置也是从0开始计算
案例:数组去重
遍历旧数组,然后去查询新数组,如果新数组中没有那就存入新数组。
调用indexof来判断元素是否存在。
function unique(arr) {
var newArr = [];
for (var i = 0;i<arr.length;i++)
{
if(newArry.indexof(arr[i]) === -1) {
newArry.push(arr[i]);
}
}
return newArry;
}
3.5数组转字符串
1.toString
var arr = [1,2,3];
console.log(arr.toString());
2.join(分隔符)
var arr = [1,2,3];
console.log(arr1.join());//output 1,2,3
console.log(arr1.join(-));//output 1-2-3
console.log(arr1.join(&));//output 1&2&3
可以自定义分隔符。
4.字符串对象
基本包装类型
对象有属性和方法。复杂数据类型有属性与方法。对象是复杂数据类型的一种。
基本包装类型,就是把简单数据类型,包装成复杂数据类型。
把简单数据类型包装为复杂数据类型 加了new的就是复杂的。
为了方便操作基本数据类型,JavaScript 还提供了三个特殊的引用类型:String、Number和 Boolean。
步骤1生成临时变量 把简单包装成复杂
var temp = new String(‘andy’);
步骤2 赋值给我们声明的字符变量
str = temp;
步骤3 销毁临时变量
temp = null;
字符串的不可变
指的是里面的值不可变,虽然看上去可以改变内容,但其实是地址变了,内存中新开辟了一个内存空间。
当重新给字符串变量赋值的时候,变量之前保存的字符串不会被修改,依然在内存中重新给字符串赋值,会重新在内存中开辟空间,这个特点就是字符串的不可变。
由于字符串的不可变,在大量拼接字符串的时候会有效率问题
根据字符返回位置
字符串通过基本包装类型可以调用部分方法来操作字符串,indexOf(要查找的字符,开始查找的位置)
案例:查找字符串"abcoefoxyozzopp"中所有o出现的位置以及次数
-
先查找第一个o出现的位置
-
然后 只要indexOf 返回的结果不是 -1 就继续往后查找
-
因为indexOf 只能查找到第一个,所以后面的查找,利用第二个参数,当前索引加1,从而继续查找
var strNum = 'abbcopoiouothjkkk'; var o = {}; var index = strNum.indexOf('o'); while (index !== -1){ console.log(index); index = strNum.indexOf('o',index+1); //每找到一次,就重新设定一次起始查找位置。 }
根据位置返回字符
字符串通过基本包装类型可以调用部分方法来操作字符串,以下是根据位置返回指定位置上的字符:charAt(index),charCodeAt(index)
charAt 根据位置返回字符
str.charAt(3);
遍历所有字符,可以用charAt
for (var i = 0;i<str.length;i++){
console.log(str.charAt(i));
}
在上述方法中,charCodeAt方法返回的是指定位置上字符对应的ASCII码.
案例:判断一个字符串 ‘abcoefoxyozzopp’ 中出现次数最多的字符,并统计其次数
-
核心算法:利用 charAt() 遍历这个字符串
-
把每个字符都存储给对象, 如果对象没有该属性,就为1,如果存在了就 +1
-
遍历对象,得到最大值和该字符
注意:在遍历的过程中,把字符串中的每个字符作为对象的属性存储在对象总,对应的属性值是该字符出现的次数
var strNum = 'abbcopoiouothjkkk';
var o = {};
for (var i = 0;i < strNum.length;i++){
var chars = strNum.charAt(i);
if (o[chars]) { //相当于把 chars 当作了对象O的一个属性
o[chars]++;
} else {
o[chars] = 1;
}
}
console.log(o);
var max = 0;
var ch = '';
for (var k in o) {
if (o[k] > max){
max = o[k];
ch = k;
}
}
console.log(max)
console.log(ch);
字符串操作方法
字符串通过基本包装类型可以调用部分方法来操作字符串
concat()
拼接字符串
replace()方法
replace() 方法用于在字符串中用一些字符替换另一些字符,其使用格式如下:
字符串.replace(被替换的字符串, 要替换为的字符串);
while (strNum.indexOf('o') !== -1) {
strNum = strNum.replace('o','*');
}
console.log(strNum);
split()方法
split()方法用于切分字符串,它可以将字符串切分为数组。在切分完毕之后,返回的是一个新数组。
其使用格式如下:
字符串.split("分割字符")
简单数据类型与复杂数据类型
2.1 简单数据类型
简单类型(基本数据类型、值类型):在存储时变量中存储的是值本身,包括string ,number,boolean,undefined,null。
null 返回的是一个空对象,不是null。
var timer = null
console.log(typeof(timer))
结果为: “object”
2.2 复杂数据类型
复杂数据类型(引用类型):在存储时变量中存储的仅仅是地址(引用),通过 new 关键字创建的对象(系统对象、自定义对象),如 Object、Array、Date等;
2.3 堆栈
js没有堆和栈
- 堆栈空间分配区别:
1、栈(操作系统):由操作系统自动分配释放存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈;
简单数据类型存放到栈里面
2、堆(操作系统):存储复杂类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。
-
简单数据类型的存储方式
值类型变量的数据直接存放在变量(栈空间)中。
-
复杂数据类型的存储方式
引用类型变量(栈空间)里存放的是地址,真正的对象实例存放在堆空间中。
2.4 简单类型传参
函数的形参也可以看做是一个变量,当我们把一个值类型变量作为参数传给函数的形参时,其实是把变量在栈空间里的值复制了一份给形参,那么在方法内部对形参做任何修改,都不会影响到的外部变量。
function fn(a) {
a++;
console.log(a);
}
var x = 10;
fn(x);
console.log(x);
// result 11
// result 10
2.5 复杂数据类型传参
函数的形参也可以看做是一个变量,当我们把引用类型变量传给形参时,其实是把变量在栈空间里保存的堆地址复制给了形参,形参和实参其实保存的是同一个堆地址,所以操作的是同一个对象。
function Person(name) {
this.name = name;
}
function f1(x) { // x = p
console.log(x.name); // 2. 这个输出什么 ?
x.name = "张学友";
console.log(x.name); // 3. 这个输出什么 ?
}
var p = new Person("刘德华");
console.log(p.name); // 1. 这个输出什么 ?
f1(p);
console.log(p.name); // 4. 这个输出什么 ?