一、数组
1.数组的创建
1.1 使用字面量创建数组
var arr = [1, 2, 3];
1.2 使用Array创建
没有参数时,空数组
参数只有一个且为数字,则创建的是num长度的数组
参数只有一个但不是数字,则将参数放入数组中作为元素。
多个参数,放入数组中作为元素。
var arr1 = new Array();
var arr2 = new Array(6);
var arr3 = new Array('尔尔er')
var arr4 = new Array(1, 2, 3);
1.3 使用Array.of创建
与Array创建的区别在于参数为1个时,也是作为数组的元素放入 数组
var arr = Array.of(1);[1]
var arr = Array.of(1,2,3);[1,2,3]
2.数组的属性和方法
增删改:
- push
向数组的末尾追加元素,可追加多个
使用方法:arr.push(增加的元素)
返回值:追加元素后,数组的长度
是否改变原数组:改变
var arr = [1, 2, 3];
console.log(arr.push(4, 5));
- pop
删除数组的最后一项
使用方法:arr.pop()
返回值:被删除的元素
是否改变原数组:改变
var arr = [1, 2, 3];
console.log(arr.pop());
- unshift
向数组的开头添加一个或多个元素
使用方法:arr.unshift(-1,0)
返回值:添加后数组的长度
是否改变原数组:改变
var arr = [1, 2, 3];
console.log(arr.unshift(0, -1));
- shift
删除数组的开头项
使用方法:arr.shift()
返回值:被删除的元素
是否改变原数组:改变
var arr = [1, 2, 3];
console.log(arr.shift());
- splice
实现数组指定位置的增加、修改、删除
使用方法:arr.splice(n,m,x)
返回值:将删除的部分用数组存储起来返回
是否改变原数组:改变
增加:①数组末尾增加x项arr.splice(arr.length, 0, x);
②数组开始位置增加arr.splice(0,0,x)
//增加
var arr1 = ['a','b','c','d'];
console.log(arr1.splice(4, 0, 'e','f'));
console.log(arr1.splice(0,0,'g','h'));
console.log(arr1);
删除:①arr.splice(0) 清空数组
②arr.splice(arr.length-1) 删除最后一项
③arr.splice(0,1) 删除第一项
④删除指定位置arr.splice(要删除位置的索引,要删除的长度)
var arr1 = ['a','b','c','d','e','f'];
// console.log(arr1.splice(0));
console.log(arr1.splice(arr1.length-1));
console.log(arr1.splice(0,1));
console.log(arr1.splice(1,2));
修改:x回替换掉m
var arr2 = [1, 2, 3];
console.log(arr2.splice(1,1,200));
console.log(arr2);
删除数组末尾项的方法:
①delete arr[arr.length-1] 删除后不会改变数组长度
②arr.length–
③arr.pop()
④arr.splice(arr.length–)
向数组末尾追加的方法:
①arr.push(‘要增加的元素’)
②arr[arr.length]=‘要增加的元素’
③arr.splice(arr.length,0,‘要增加的元素’)
查询和拼接
silce
slice(start,end)
用于实现数组的查询,两个参数,从索引start开始,到索引end结束(不包含end,[start,end)), 将查询到的内容以新数组的形式返回。不改变原数组
var arr = [2, 1, 6, 9, 0];
console.log(arr.slice(1, 3));
concat
arr.concat(拼接的内容)
实现数组的拼接,可拼接多个,返回值为拼接后的新数组
var arr1 = [1, 2, 3];
var arr2 = [4, 5, 6];
console.log(arr1.concat(arr2));
数组转字符串
toString
将数组转为字符串,没有参数,不改变原数组,转换后的字符串每一项用逗号分隔
var arr = [1,2,3,4];
console.log(arr.toString());
join
将数组转为字符串,参数为指定的分隔符,未指定分隔符则用逗号分隔,指定的分隔符为空字符,则将每项连接起来。
var arr = [1, 2, 3];
console.log(arr.join());//1,2,3
console.log(arr.join(''));//123
检测数组中是否包含某一项
indexOf
检测当前项在数组中第一次出现位置的索引值,返回值为索引值
lastIndexOf
检测当前项在数组中最后一 次出现位置的索引值,返回值为索引值
includes
检测数组中是否包含某项,有则返回true,反之返回false
var arr = [1, 2, 3, 4, 1];
console.log(arr.indexOf(1));
console.log(arr.lastIndexOf(1));
console.log(arr.includes(1));
排序、排列
reverse
把数组翻转过来,改变原数组
var arr = [1, 5, 3, 5, 2, 5];
console.log(arr.reverse());
console.log(arr);
sort
实现数组的排序,改变原数组
var arr = [1, 5, 3, 5, 2, 30];
console.log(arr.sort());
console.log(arr);
升序
var num1 = [3,1,20,5,4,6];
function compare(a, b) {
if (a < b) {
return -1;
}else if(a > b) {
return 1;
}else{
return 0;
}
}
num1.sort(compare);
console.log(num1);
降序
function compare1(a, b) {
if (a < b) {
return 1;
}else if(a > b) {
return -1;
}else{
return 0;
}
}
num1.sort(compare1);
console.log(num1);
find
判断一个变量是否是一个数组
①isArray()判断
②利用变量原型的构造函数是不是Array来判断
var arr = [1,2,3,4];
console.log(Array.isArray(arr))
console.log(arr.__proto__.constructor === Array)
3.数组的遍历
forEach
针对每一个元素执行提供的函数
var arr = [1, 2, 3, 4, 5];
arr.forEach(function(item,ind){
console.log(item);
});
map
创建一个新的数组,其中每一个元素由调用数组中的每一个元素执行提供的函数得来。
var names = [1, 2, 3, 4, 5, 6, 7];
// map : 会将函数的返回值组成一个新的数组作为 map的返回值
var re = names.map(function (item, index, arr) {
item += 2;
return item;
});
console.log(re);
console.log(names);
兼容性处理
forEach和map的区别:
forEach()方法不会返回执行结果,而是undefined。而map()方法会得到一个新的数组并返回。
4.类数组
类数组具有length属性,类数组没有数组的方法。
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
</ul>
let lis = document.querySelectorAll('li');
console.log(lis);
// lis.push(2);//arr2.push is not a function
console.log(Array.isArray(lis));
类数组转数组:
1)call函数让类数组Array原型的slice方法,截取数组所有项组成一个新数组
2) apply函数修改指向,将空数组和类数组进行连接组成一个新数组
3)使用**Array.from()**可以将类数组转为数组,返回值为一个新的数组
4)…运算符
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
</ul>
let lis = document.querySelectorAll('li');
console.log(lis);
var arr1 = Array.prototype.slice.call(lis);
arr1.push('我是新增的');
console.log(arr1);
var arr2 = Array.prototype.concat.apply([], lis);
arr2.pop();
console.log(arr2);
var arr4 = Array.from(lis);
arr4.push('hhh');
console.log(arr4);
console.log([...lis]);
5.二维数组和多维数组
一维数组:arr[]
二维数组:arr[[ ], [ ]]
三维数组: arr[[[ ] ], [ [ ]]]
var arr = [
[1,11,111],
[2,22,222],
[3]
]
console.log(arr[1][2]);
//二维数组的遍历
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length; j++) {
console.log(arr[i][j]);
}
}
//用forEach遍历数组
arr.forEach(function(value){
value.forEach(function(item){
console.log(item);
})
})
二、对象
1.对象的创建
字面量创建
var person1 = {
name: "wu",
age: 20,
job: "Software Engineer",
sayName: function () {
alert(this.name);
}
};
console.log(person1);
工厂模式
function createAnimal(name, age, jiao) {
var o = {}
o.name = name;
o.age = age;
o.jiao = jiao;
o.sayName = function () {
alert(this.name);
}
return o;
}
var cat = createAnimal("元宝", 2, "maio~~~");
console.log(cat);
var dog = createAnimal("球球", 3, "汪~~~");
console.log(dog.sayName());
构造函数创建
function Animal(name, age, jiao) {
this.name = name;
this.age = age;
this.jiao = jiao;
this.sayName = function () {
alert(this.name);
}
}
var cat = new Animal("元宝", 2, "maio~~~");
console.log(cat);
var dog = new Animal("球球", 3, "汪~~~");
dog.sayName();
工厂模式和构造函数模式的异同点
共同点:都是函数 ,都可以创建对象,都可以传参
区别:
工厂模式:
1.函数名是小写
2.没有new,
3.需要return语句
4.直接调用函数就可以创建对象
自定义构造函数:
1.函数名是大写(首字母)
2.有new
3.不需要return语句
4.通过new的方式来创建对象
new创建
var obj = new Object();
obj.name = "wu";
console.log(obj.name);
var obj2 = new Date();
console.log(obj2);
Object.create()创建
将 {name: ‘zhang’} 作为 新对象的原型, 保存在__proto__
中
obj 访问 name属性, 自己没有则沿着原型链开始向上寻找, 直到找到返回值, 或 没有找到返回undefined
var obj3 = Object.create({name: 'wu'})
console.log(obj3.name);
console.log(obj3.age);//undefined
2对象的方法
valueOf()返回当前对象原始值
var o = new Object();
o.valueOf() === o //true
toString()方法返回当前对象对应的字符串形式
var o1 = new Object();
o1.toString()
var o2 = {a:1};
o2.toString()
//函数调用该方法返回的是函数本身的代码
function hq (argument) {
// body...
}
console.log(hq.toString());
var a = [1,2,3];
console.log(a.toString());
console.log(new Date().toString())
Object | 行为 |
---|---|
数组 | 将Array的元素转换为字符串,结果字符串被连接起来,用逗号分隔 |
布尔值 | 如果布尔值为true,则返回"true",否则返回"false" |
日期 | 返回日期的文本表示形式 |
错误 | 返回一个包含相关错误信息的字符串 |
函数 | 返回如下格式的字符串,其中functionName是函数的名称 function functionName() { [native code] } |
Number | 返回数字的文字表示形式 |
字符串 | 返回String对象的值 |
默认{} | 返回"[object Object]" |
对象和字符串之间的转换
var o1 ={
name:"o1"
}
console.log(JSON.stringify(o1));
console.log(JSON.parse(JSON.stringify(o1)) === o1); // false
3.对象的属性
delete
delete可以删除对象自身的属性,不能删除继承来的属性
var obj = {
name:"wu",
age: 20
}
delete obj.name;
console.log(obj);
delete obj.toString;
console.log(obj.toString);
判断一个对象中是否含有某个属性
- 使用in判断
既可以判断自身的属性,也可以判断继承来的属性 - 使用hasOwnProperty判断
只能判断自身有的属性,不能判断继承来的属性
3)propertyIsEnumerable()
检查一个属性是否属于某个对象自有属性,不包括继承来的属性,且该属性可枚举
//检测属性
var person = {name:"尔尔er", age: 20}
//设置属性,并配置信息
Object.defineProperty(person, "sex",{
value:"女",
enumerable:false
});
//in
console.log('name' in person);//true
console.log('sex' in person);//true
console.log('toString' in person);//true
//hasOwnProperty()
console.log(person.hasOwnProperty('name'));//true
console.log(person.hasOwnProperty('sex'));//true
console.log(person.hasOwnProperty('toString'));//false
//propertyIsEnumerable()
console.log(person.propertyIsEnumerable('name'));//true
console.log(person.propertyIsEnumerable('sex'));//false
console.log(person.propertyIsEnumerable('toString'));//false
delete可以删除对象自身的属性,不能删除继承来的属性
内置Object的defineProperty
有三个参数:操作的对象,要操作的属性(String类型),属性描述(对象类型)
var obj = {name:'尔尔' ,age:18};
function showData(data){
document.querySelector('.name').innerHTML = obj.name;
document.querySelector('.age').innerHTML = obj.age;
document.querySelector('.gender').innerHTML = obj.gender;
}
showData(obj);
setInterval(function(){
obj.gender = Math.random();
}, 1000)
Object.defineProperty(obj, 'gender' , {
enumerable:true,
configurable:true,
get: function(){
return gender;
},
set: function(newValue){
gender = newValue;
showData(obj);//改变数据,页面上的数据更新显示
}
})
obj.gender = '男';
三、函数
1.函数的三种定义方式
1.1函数声明
function fun(){
console.log(1);
}
1.2函数表达式定义
var foo = function(){
console.log(1);
}
1.3内置构造函数定义
不常用,无法实现递归(自己调用自己)
var f = new Function('x,y', 'console.log(x,y);')
2.函数调用
函数中的this指向函数的调用者,谁调用这个函数this就指向谁。this的指向和定义的位置无关,只和谁调用有关。
function f1(){
console.log(this);
}
var o1 = {
foo:function(){
console.log(this);
},
fun:f1,
obj:{
f3:f1
}
}
o1.foo();//o1
o1.fun();//o1
o1.obj.f3();//obj
函数调用
使用函数调用,this指向的是window
function f1(){
console.log(this);
}
f1();//Window
方法调用
this指向调用该方法的对象
function f1(){
console.log(this);
}
var obj = {
f2:f1
}
obj.f2();//obj
3.this关键字
函数在执行的时候,函数内部会生成一个this。这个this指向谁在函数定义的时候是不确定的,函数执行时才能确定,谁调用它this就指向谁。
普通函数执行时,this指向的是全局对象window,函数作为对象中的方法调用时,this则指向调用这个方法的对象。
function fn(){
console.log(this);
}
fn();
var obj = {
name:'尔尔er',
say: function(){
console.log(this);
}
};
obj.say();
4.构造函数
- 通过new 函数名来实例化对象的函数叫构造函数。构造函数就是函数,函数就是构造函数,使用function来声明。构造函数和普通函数的区别在于功能,构造函数主要用于实例化对象。
2)在构造函数里面使用this指向构造函数的实例化对象
3)使用new关键字来创建一个实例化对象
// 构造函数
function Cat(name,gender,age){
this.name = name;
this.gender = gender;
this.age = age;
this.say = function(){
return `我叫${this.name},我是${this.gender},我${this.age}岁啦`;
}
}
// 使用new创建一个Cat的实例化对象
var cat = new Cat('元宝','弟弟',1);
var cat2 = new Cat('蓝宝','弟弟',2);
console.log(cat.say == cat2.say);//false
使用构造函数存在一个问题,每创建一个实例化对象,每个方法都要在每个实例上重新创建一遍,即在构造函数的不同实例上的同名函数是不相等的。
通过构造函数的原型prototype来扩展属性或方法
function Cat(name,gender,age){
this.name = name;
this.gender = gender;
this.age = age;
}
Cat.prototype.say = function(){
return `我叫${this.name},我是${this.gender},我${this.age}岁了`;
}
var cat = new Cat('元宝','弟弟',1);
var cat2 = new Cat('蓝宝','弟弟',2);
console.log(cat.say == cat2.say);//true
new关键字的作用
①分配一片空间
②让this指向这片空间(构造函数的实例)
③添加默认属性__proto__:{}
④在结尾默认返回this
5.方法劫持
作用:可以改变this的指向
call
两个或多个参数,第一个参数定义this的指向,后面的参数用于传参,使用散列的方式进行传参,函数立即执行。
apply
两个参数,第一个参数定义this的指向,第二个的参数用于传参,使用数组进行传参,函数立即执行。
bind
两个或多个参数,第一个参数定义this的指向,后面的参数用于传参,使用散列的方式进行传参。返回函数本身。bind的时候传的参数会预先传给返回的方法,调用方法时就不用再传参数了。
var cat = {
action: function(name,age){
console.log(`我叫${name},我${age}岁了,我要洗澡`);
}
};
var dog = {
};
cat.action.call(dog,'南瓜',2);
cat.action.apply(dog,['南瓜',2]);
var dog1 = cat.action.bind(dog,'南瓜',2);
dog1();
4.闭包
函数嵌套函数,参数和变量不会被垃圾回收机制回收
function fn(){
var i = 1;
return function(){
console.log(i++);
}
}
//引用在,空间不灭
var f = fn();
f();
f();
四、原型和原型链
1) 所有的对象(null除外)都有一个属性__proto__
,这个属性会指向该对象的原型。
2)构造函数有一个特有的属性prototype,指向他的的原型
3)在调用一个对象的属性和方法的时候,如果他自身没有这个属性或方法,就会在自己的原型(__proto__
)上面找,如果没有找到,则会继续顺着__proto__
原型去找,直到找到为止。
4)实例化对象的原型(__proto__
)指向构造函数的prototype
5) 所有构造函数本身也是一个实例化对象,是Function这个内置的构造函数的实例化对象。
6)顶层对象是window
7)全局对象是GlobalThis
- js中一切皆为对象
__proto__
和prototype的区别
__proto__
是对象的内置属性,而prototype是函数的内置属性
可以使用isPrototypeOf()方法判断一个对象是否为参数对象的原型
var proto = Object.prototype;
console.log(proto.isPrototypeOf({}));
原型链
实例对象的__proto__
属性指向其对应的原型对象。而在原型对象prototype
上又有constructor
和__proto__
属性,此时的__proto__
又指向上级对应的原型对象,最终指向Object.prototype
, 而Object.prototype.__proto__ === null
。这就构成了原型链,而原型链最终都是指向null。当访问一个实例化对象的属性或方法时,会优先访问自己的,如果它自身没有,则沿着原型链一层一层找。
function Person(){
}
var person1 = new Person();
console.dir(Person);
console.log(Person.prototype === person1.__proto__);//true
console.log(Person.__proto__ === Function.prototype);//true
console.log(Function.__proto__ === Function.prototype);//true
console.log(Person.prototype.__proto__ === Object.prototype);//true
console.log(Object.__proto__ === Function.prototype);//true
console.log(Object.prototype.__proto__);//null
通过构造函数创建的对象–>函数原型–>Object.prototype–> null 的指向,就是所谓的原型链;
五、JSON
JSON.stringfy()可以将JSON格式的数据序列化成字符串
JSON.parse()将string格式的数据解析成JSON格式
var obj = {
code: 200,
error: 0,
msg: 'SUCCESS',
data: [{
name: '尔尔er',
age: 20
}, {
name: '尔尔er',
age: 20
}, {
name: '尔尔er',
age: 20
}]
};
// window.localStorage将数据缓存到本地
window.localStorage.setItem('data', JSON.stringify(obj));
console.log(localStorage.getItem('data'));
console.log(JSON.parse(localStorage.getItem('data')));