Number、String、Boolean、Undefined、Null
Number类型的进制
-
十进制
进行算数计算时,八进制和十六进制表示的数值最终都将被转换成十进制数值。
-
八进制
数字序列范围:0~7
如果字面值中的数值超出了范围,那么前导零将被忽略,后面的数值将被当作十进制数值解析
// 对应十进制的7 var num1 = 07; // 对应十进制的19,因为出现了数字9,所以已经超过了八进制,所以最开始位的0失效,八进制变成了十进制的 19 var num2 = 019; // 对应十进制的8 var num3 = 08;
-
十六进制
数字序列范围:0
9以及AF// 定义了一个16进制的数据 var num = 0xA;
浮点数精度问题
==不要直接判断两个浮点数是否相等==
// 浮点数
var n = 5e-324; // 科学计数法 5乘以10的-324次方
// 浮点数值的最高精度是 17 位小数,但在进行算术计算时其精确度远远不如整数
var result = 0.1 + 0.2; // 结果不是 0.3,而是:0.30000000000000004
var a = 0.3
console.log( a == result); // fasle
数值判断
NaN:not a number NaN 与任何值都不相等,包括他本身
==不要用NaN验证是不是NaN==
var num;
console.log(num + 10 == NaN); // false ,不能用 NaN 来验证值是不是NaN
// 验证结果是不是NaN,应该使用isNaN()
isNaN: is not a number
==判断结果不是一个数字可以使用isNaN(变量名)==
var num;
var sum = num + 10;
console.log(sum); // NaN
// 不是数字为true,是数字结果为false
console.log(isNaN(sum)); // true ,结果不是数字
用length()来判断字符串长度
var str = '黑马程序猿 Hello World';
console.log(str.length);
- Boolean字面量: true和false,区分大小写
- 计算机内部存储:true为1,false为0
-
undefined表示一个声明了没有赋值的变量,变量只声明的时候值默认是undefined
undefined: 未定义,值只有一个:undefined
什么情况下的结果是undefined 变量声明了,没有赋值,结果是undefined 函数没有明确返回值,如果接收了,结果也是undefined
// 下面的变量只声明没有赋值 var num; // 变量只声明的时候值默认是undefined console.log(num); // undefined // 如果一个变量的结果是undefined和一个数字进行计算,结果:NaN不是一个数字,也没有意义 console.log(num + 10); // NaN
-
null表示一个空,变量的值如果想为null,必须手动设置
null:空类型,值只有一个:null,一个对象指向为空了,此时可以赋值为null
var nll = null; // 手动赋值 null 给变量 nll
Object
var obj = new Object(); // 赋值了复杂的数据类型
-
typeof 变量名
-
typeof (变量名)
var num = 10;
var str = "小白";
var flag = true;
var nll = null;
var undef;
var obj = new Object();
// 是使用typeof 获取变量的类型
console.log(typeof num); // number
console.log(typeof str); // string
console.log(typeof flag); // boolean
// 有些值没有办法通过tostring()的方式转换成字符串,这个时候需要通过String()的方式进行转换
console.log(String(nll)); // null
console.log(typeof nll); // object
console.log(typeof undef); // undefined
console.log(typeof obj); // object
console.log(typeof(num)); // number
==如果变量有意义调用.toString()使用转换==
==如果变量没有意义使用String()转换==
-
toString()
var num = 5; console.log(num.toString());
-
String()
String()函数存在的意义:有些值没有toString(),这个时候可以使用String()。比如:undefined和null
var nll = null; console.log(String(nll)); // null var num; console.log(String(num) == "undefined"); // true
-
拼接字符串方式
num + "",当 + 两边一个操作符是字符串类型,一个操作符是其它类型的时候,会先把其它类型转换成字符串再进行字符串拼接,返回字符串
-
Number()
Number()可以把任意值转换成数值,如果要转换的字符串中有一个不是数值的字符,返回NaN
console.log(Number("10")); // 10 console.log(Number("10afrswfdsf")); // NaN console.log(Number("g10")); // NaN console.log(Number("1fds0")); // NaN console.log(Number("10.98")); // 10.98 console.log(Number("10.98fdsfd")); // NaN
-
parseInt()
var num1 = parseInt("12.3abc"); // 返回12,如果第一个字符是数字会解析知道遇到非数字结束 var num2 = parseInt("abc123"); // 返回NaN,如果第一个字符不是数字或者符号就返回NaN
-
parseFloat()
parseFloat()把字符串转换成浮点数 parseFloat()和parseInt非常相似,不同之处在与 parseFloat会解析第一个. 遇到第二个.或者非数字结束 如果解析的内容里只有整数,解析成整数
console.log(parseFloat("10")); // 10 console.log(parseFloat("10afrswfdsf")); // 10 console.log(parseFloat("g10")); // NaN console.log(parseFloat("1fds0")); // 1 console.log(parseFloat("10.98")); // 10.98 console.log(parseFloat("10.98fdsfd")); // 10.98
-
+,-0等运算
var str = '500'; console.log(+str); // 取正 console.log(-str); // 取负 console.log(str - 0);
0 ' '(空字符串) null undefined NaN 会转换成false 其它都会转换成true
console.log(Boolean(1)); // true
console.log(Boolean(0)); // false
console.log(Boolean(11)); // true
console.log(Boolean(-10)); // true
console.log(Boolean("哈哈")); // true
console.log(Boolean("")); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
-
算术运算符
+ - * / %
前置++
先加1,后参与运算
后置++
先参与运算,后加1
-
逻辑运算符
&& 与 两个操作数同时为true,结果为true,否则都是false || 或 两个操作数有一个为true,结果为true,否则为false ! 非 取反
-
关系运算符(比较运算符)
< > >= <= == != === !==
==与===的区别:==只进行值得比较,===类型和值同时相等,则相等 var result = '55' == 55; // true var result = '55' === 55; // false 值相等,类型不相等 var result = 55 === 55; // true
-
赋值运算符
= += -= *= /= %=
var num = 0; num += 5; //相当于 num = num + 5;
1.所谓数组,就是将多个元素按一定顺序排列放到一个集合中,那么这个集合我们就称之为数组。数组一般会存放同样类型的数据,Js中的数组可以存放不同类型的数据.
2.数组是一个有序的列表,可以在数组中存放任意的数据,并且数组的长度可以动态的调整。
-
构造函数的方式
// 创建一个空数组 var arr1 = new Array(); // 创建一个放有三个字符串的数组 var arr = new Array('zs', 'ls', 'ww');
-
字面量的方式创建数组
// 创建一个空数组 var arr2 = []; // 创建一个放有三个字符串的数组 var arr2 = new Array('zs', 'ls', 'ww');
判断一个数据是不是数组
- Array.isArray()
- instanceof
var arr1 = new Array();
var arr = new Array('zs', 'ls', 'ww');
console.log(Array.isArray(arr1)); // true
console.log(Array.isArray(arr2)); // true
console.log(arr1 instanceof Array); // true
console.log(arr2 instanceof Array); // true
// 格式:数组名[下标] 下标又称索引
// 功能:获取数组对应下标的那个值,如果下标不存在,则返回undefined。
var arr = ['red', 'green', 'blue'];
arr[0]; // red
arr[2]; // blue
arr[3]; // 这个数组的最大下标为2,因此返回undefined
- 普通的元素遍历
for(var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
- 通过回调函数的方式遍历
var arr = ["a","b","c"];
arr.forEach(function(ele){
console.log(ele + "你好"); // a你好 b你好 c你好
});
// 格式:数组名[下标/索引] = 值;
// 如果下标有对应的值,会把原来的值覆盖,如果下标不存在,会给数组新增一个元素。
var arr = ["red", "green", "blue"];
// 把red替换成了yellow
arr[0] = "yellow";
// 给数组新增加了一个pink的值
arr[3] = "pink";
将一个类数组对象或者可遍历对象转换成一个真正的数组。
-
将类数组对象转换为真正数组
// 类数组对象,最基本的要求就是具有length属性的对象。 let arrayLike = { 0: 'tom', 1: '65', 2: '男', 3: ['jane','john','Mary'], // 因为指定了length属性,所以这个对象是一个类属性对象 'length': 4 } // 将类属性对象转化成数组 let arr = Array.from(arrayLike) console.log(arr) // ['tom','65','男',['jane','john','Mary']]
- length属性的值决定了生成数组的长度
- 如果类数组对象不带length属性,那么转换出来就是空数组
let arrayLike = { 0: 'tom', 1: '65', 2: '男', 3: ['jane','john','Mary'], // 这里将类数组的属性设置为3,所以只会生成一个长度为3的数组 // ['jane','john','Mary']不会作为元素被生成 'length': 3 } // 将类属性对象转化成数组 let arr = Array.from(arrayLike) console.log(arr) // ['tom','65','男'] 生成的数组的长度为 3
- 类数组对象的属性名必须为数值型或字符串型的数字,否则结果会是 undefined
let arrayLike = { 'name': 'tom', // 3 : '65', 数值型和字符串型的数字都是可以当做属性名的 '3' : '65', // 属性名意味着属性值在数组中的位置,因为属性名是3,所以 "65" 在数组的最末端 'sex': '男', 'friends': ['jane','john','Mary'], length: 4 } let arr = Array.from(arrayLike) console.log(arr) // [undefined, undefined, undefined, "65"]
- 有length属性,空余的索引值将会是undefined
let json = { 0:"a", 1:"b", 2:"c", 3:"zk", four:"4", 5:"zs", length:7, } let arr = Array.from(json); console.log(arr); // ["a", "b", "c", "zk", undefined, "zs", undefined]
-
将字符串转换为数组
let str = 'hello world!'; console.log(Array.from(str)) // ["h", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d", "!"]
-
复制数组,如果传的是数组,将把数组复制一份传给新数组
let arr = [1,2,3,4,5]; let arr2 = Array.from(arr); console.log(arr) // [1,2,3,4,5] console.log(arr2) // [1,2,3,4,5]
-
数组去重
let arr = [1,1,"six","six",null,null,undefined,undefined,false,false,true,true,"",""]; // new Set 是实现数组去重, // Array.from()把去重之后转换成数组 let arr2 = Array.from(new Set(arr)); console.log(arr2); // [1, "six", null, undefined, false, true, ""]
-
对数组中的每一个元素进行处理
Array.from
还可以接受第二个参数,作用类似于数组的map
方法,用来对每个元素进行处理,将处理后的值放入返回的数组let arr = [12,45,97,9797,564,134,45642] let set = new Set(arr) console.log(Array.from(set, item => item + 1)) //[ 13, 46, 98, 9798, 565, 135, 45643 ]
-
将伪数组转成数组在赋给新数组
// html代码 <ul> <li>1111</li> <li>2222</li> <li>3333</li> <li>4444</li> <li>5555</li> </ul> // 得到的是伪数组 let oli = document.querySelectorAll("li"); console.log(oli); //NodeList(5) [li, li, li, li, li] // 对伪数组NodeList()进行转换 let arr2 = Array.from(oli); console.log(arr2); //(5) [li, li, li, li, li]
concat(数组,数组,数组,...) 组合一个新的数组
var arr1 = [10,20,30];
var arr2 = [40,50,60];
console.log(arr1.concat(arr2)); // 组成了新的数组 [10,20,30,40,50,60]
回调函数的方式快速遍历一个数组
var aaa = ["1","2","3"];
// ele就是 数组 中的每一个元素
aaa.forEach(function(ele){
console.log(ele + "你好");
})
every(函数)--返回值是布尔类型
函数作为参数使用,函数中有三个参数,第一个参数是元素的值,第二个参数是索引值,第三个参数是原来的数组(没用), 如果这个数组中的每个元素的值都符合条件,最后才返回的是true
var arr=[1000,2000,3000];
// 第一个参数为数组中元素的值,第二个参数为数组中元素的索引
var flag = arr.every(function(element,index){
// 必须每一个元素都要大于2000,才能返回true,否则只能返回false
return element > 2000;
})
console.log(flag); // false
var arr=["小明明lkko","小曹操674","小白白bd","笑眯眯a"];
var flag = arr.every(function(ele,index){
return ele.length > 3;
})
console.log(flag); // true
filter(函数);返回的是数组中每一个元素都复合条件的元素,组成了一个新的数组
var arr = [10,20,30,40,50,60,70,80];
// 第一个参数ele代表每一个参数
var newArr = arr.filter(function(ele){
// 要求每一个参数都要大于 40
return ele > 40;
})
console.log(newArr); // 组成的新的数组: [50,60,70,80]
// 过滤出 0
var arr = [10,0,20,0,40,0,60,100];
var newArr = arr.filter(function(ele){
// 过滤出不为0的数据
return ele != 0;
})
console.log(newArr); // [10,20,40,60,100]
push(值) 把值追加到数组中,加到最后了--->返回值也是追加数据之后的数组长度
var arr = [1,2,3,4]
console.log(arr.push("我")); // 新数组的长度为: 5
console.log(arr); // [1,2,3,4,"我"] 添加之后的新数组
pop();--->删除数组中最后一个元素,返回值就是删除的这个值
var arr = [1,2,3,4,"我"];
console.log(arr.pop()); // 我
console.log(arr); // [1,2,3,4]
shift();--->删除数组中第一个元素,返回值就是删除的这个值
var arr = [1,2,3,4];
console.log(arr.shift()); // 1
console.log(arr); // [2,3,4]
unshift();--->向数组的第一个元素前面插入一个新的元素,----返回值是插入后的长度
var arr = [2,3,4];
console.log(arr.unshift("你好")); // 4 数组的长度
console.log(arr); // ["你好",2,3,4]
indexOf(元素值);返回的是索引,没有则是-1
var arr = [1,2,3,4,5,6]
// 返回了 元素值为 6 的索引
var index1 = arr.indexOf(6);
// 没有 60 这个元素值
var index2 = arr.indexOf(60);
console.log(index1); // 5
console.log(index2); // -1
join("字符串");----返回的是一个字符串
var arr = ["小白","小黑","小红","小芳","小绿","小苏"];
// 通过&插入到数组当中,已经由数组变成了字符串
var str = arr.join("&");
console.log(str); // 小白&小黑&小红&小芳&小绿&小苏
console.log(typeof str); // String
map(函数);--->数组中的每个元素都要执行这个函数,
把执行后的结果重新的全部的放在一个新的数组中
var arr = [0.1,0.2,0.3,0.4];
var new_arr = arr.map(function(x){
// 自定义函数的话,必须要有返回值
return x * 10;
})
console.log(new_arr); // [1,2,3,4]
// 使用js自带的内置函数
var arr = [1.465,2.8465,3.5,4.84];
// 对所有的元素都进行了四舍五入
var new_arr = arr.map(Math.round);
console.log(new_arr); // [1,3,4,5]
反转数组
var arr = ["er","se","wseh"];
arr.reverse();
console.log(arr); // ["wseh","se","er"]
对数组进行排序
单独使用sort方法排序,可能不稳定,我一般使用下面嵌套的方法来进行排序
var arr=[1,40,20,10,100];
arr.sort(function (a,b) {
if(a>b){
// 正序排序
return 1;
}else if(a==b){
return 0;
}else{
return -1;
}
});
console.log(arr);
arr.slice(开始的索引,结束的索引);
把截取的数组的值放在一个新的数组中,但是不包含结束的索引对应的元素值
var arr = [10,20,30,40,50,60,70,80,90,100];
var newArr = arr.slice(3,7);
// 索引7对应的数值是80,但是取80前面的那个数值
console.log(newArr); // [40,50,60,70]
splice(开始的位置,要删除的个数,替换的元素的值); 一般是用于删除数组中的元素,或者是替换元素,或者是插入元素
var myFish = ['angel', 'clown', 'mandarin', 'sturgeon'];
// 在索引为2的位置插入'drum'(因为已经把删除的个数设置为0,所以只插入)
myFish.splice(2, 0, 'drum');
console.log(myFish); // ['angel', 'clown', 'drum', 'mandarin', 'sturgeon'];
// 从索引为2的位置删除一项(也就是'drum'这一项)
myFish.splice(2, 1);
// 数组又变了回去
console.log(myFish); // ['angel', 'clown', 'mandarin', 'sturgeon']
var arr1 = [10, 20, 30, 40, 50];
// 案例1:求数组中所有元素的和
var sumArr1 = 0;
for (var i = 0; i < arr1.length; i++) {
sumArr1 += arr1[i];
}
console.log("所有元素的和" + sumArr1);
// 案例2:求数组中所有元素的平均值
var sumArr2 = 0;
for (var i = 0; i < arr1.length; i++) {
sumArr2 += arr1[i];
}
var vegArr1 = sumArr2 / arr1.length;
console.log("所有元素的平均值" + vegArr1);
// 案例3:求数组中所有元素中的最大值
var maxNum = arr1[0];
for (var i = 0; i < arr1.length; i++) {
if (maxNum < arr1[i]) {
maxNum = arr1[i]
}
}
console.log("所有元素中的最大值" + maxNum);
// 案例4:求数组中所有元素的最小值
// 案例5:倒序遍历数组
for (var i = arr1.length - 1; i >= 0; i--) {
console.log(arr1[i]);
}
//案例6:把数组中每个元素用|拼接到一起产生一个字符串并输出
var names = ["卡卡西", "佐助", "鸣人", "大蛇丸", "雏田", "小苏", "凤姐", "黑崎一护"];
var str = "";
for (var i = 0; i < names.length; i++) {
str = str + "|" + names[i];
}
console.log(str);
// 案例7:去掉数组中重复的0,把其他的数据放在一个新的数组中
var arr = [10, 0, 20, 0, 30, 0, 50];
var newArr=[];//新数组,用来存放第一个数组中所有非0的数字
for(var i=0;i<arr.length;i++){
if(arr[i]!=0){
newArr[newArr.length]=arr[i];
}
}
//把新数组的长度作为下标使用,数组的长度是可以改变的
console.log(newArr);
// 案例8:反转数组---把数组中的数据的位置调换
var arr = [10, 0, 20, 0, 30, 0, 50];
console.log(arr.reverse());
console.log("--------------------------------")
// var arr = [10, 0, 20, 0, 30, 0, 50];
for(var i = arr.length - 1;i>=0;i--){
console.log(arr[i]);
}
冒泡排序
//冒泡排序:把所有的数据按照一定的顺序进行排列(从小到大,从大到下)
// 从大到小排列
var arr = [10, 0, 100, 20, 60, 30];
for(var i = 0;i<arr.length - 1;i++){
for(var j = 0;j<arr.length - 1 - i;j++){
if(arr[j] < arr[j+1]){
var temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
};
}
console.log(arr);
// 从小到大排列
var arr = [10, 0, 100, 20, 60, 30];
for(var i = 0;i<arr.length - 1;i++){
for(var j = 0;j<arr.length - 1 - i;j++){
if(arr[j] > arr[j+1]){
var temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
};
}
console.log(arr);
声明式函数
function 函数名(){
// 函数体
}
函数表达式
var fn = function() {
// 函数体
}
匿名函数
// 匿名函数
// 第一个括号的内容是匿名函数的内容,第二个括号是用来调用函数的
(function(){
console.log("hello world!!!");
})();
调用
// 函数名加上括号的方式来调用
函数名();
函数也是一种数据类型
function fn() {}
console.log(typeof fn);
//声明一个带返回值的函数
function 函数名(形参1, 形参2, 形参...){
//函数体
return 返回值;
}
//可以通过变量来接收这个返回值
var 变量 = 函数名(实参1, 实参2, 实参3);
返回值详解
如果函数没有显示的使用 return语句 ,那么函数有默认的返回值:undefined 如果函数使用 return语句,那么跟再return后面的值,就成了函数的返回值 如果函数使用 return语句,但是return后面没有任何值,那么函数的返回值也是:undefined 函数使用return语句后,这个函数会在执行完 return 语句之后停止并立即退出,也就是说return后面的所 有其他代码都不会再执行。
==推荐的做法是要么让函数始终都返回一个值,要么永远都不要返回值。==
JavaScript中,arguments对象是比较特别的一个对象,实际上是当前函数的一个内置属性。也就是说所有函数都内置了一个arguments对象,arguments对象中存储了传递的所有的实参。arguments是一个伪数组,因此及可以进行遍历
function f1(){
// 打印传入函数的参数的长度(判断伪数组的长度)
console.log(arguments.length);
console.log(arguments);
// 遍历参数伪数组
for(var i = 0;i < arguments.length;i++){
// 打印每一个参数
console.log(arguments[i]);
}
}
// 向函数中传入参数
f1(1,2,3,4,5,6);
// 编写一个匿名函数,自己调用自己
(function(){
console.log("自己调用自己的函数");
})();
// 把一个对象当做形参传入我们定义的匿名函数中
(function(win){
var num = 10;
// js是一门动态类型的语言,对象没有属性,点了就有了
// 给我们传入的对象添加属性
// 我们传入的是windos对象,现在相当于给整个页面赋值,相当于匿名函数里面的局部变量变成了真个页面的全局变量
win.num = num;
})(window);
// 由于把局部变量的值赋值给了 Window对象,所以整个页面上都能访问到了
console.log("整个页面的值是:" + num);
产生随机数赋值给windos成为全局变量
(function(win){
// 创建一个产生随机数的函数
function createRandomNum(){
// 目前这个函数只有方法没有属性
}
// 在原型对象中添加方法
createRandomNum.prototype.getRanNum = function(max,min){
// 返回我们随机生成的随机数给方法
return Math.floor(Math.random()*(max-min)+min);
};
// 把我们创建的函数的对象赋值给windos顶级对象
// 这样整个页面就能使用我们定义的随机数函数了
window.createRandomNum = createRandomNum;
})(window);
// 实例化随机数对象那
var rm = new createRandomNum();
console.log(rm.getRanNum(0,100));
函数声明如果放在if-else的语句中,在IE8的浏览器中会出现问题
以后宁愿用函数表达式,都不用函数都不用函数式声明
// 函数声明
if(true){
function fn1(){
console.log("我是true")
}
}else{
function fn2(){
console.log("我是false");
}
};
fn1();
// 函数表达式
var ff;
if(true){
ff = function(){
console.log("haha")
}
}else{
ff = function(){
console.log("mama");
}
}
ff();
函数中的this的指向
普通函数中的this是谁?-----window
对象.方法中的this是谁?----当前的实例对象
定时器方法中的this是谁?----window
构造函数中的this是谁?-----实例对象
原型对象方法中的this是谁?---实例对象
// 函数是对象,对象不一定是函数
// 对象中有__proto__原型,是对象
// 函数中有prototype原型,是对象
function F1() {
}
console.dir(F1);
console.dir(Math); // 有__proto__,但是没有prorotype
// 对象中有__proto__,函数中应该有prototype
// 如果一个东西里面有prototype,又有__proto__,说明是函数,也是对象
function F1(name) {
this.name=name;
}
console.dir(F1);
// 所有的函数实际上都是Function的构造函数创建出来的实例对象
var f1 = new Function("num1","num2","return num1+num2");
console.log(f1(10,20));
console.log(f1.__proto__==Function.prototype);
//所以,函数实际上也是对象
console.dir(Function);
数组可以储存任何类型的数据
数组中可以存储着很多函数的对象
var arr=[
function () {
console.log("十一假期快乐");
},
function () {
console.log("十一假期开心");
}
,
function () {
console.log("十一假期健康");
}
,
function () {
console.log("十一假期安全");
},
function () {
console.log("十一假期如意");
}
];
// 回调函数,函数作为参数来使用
// 遍历整个数组,遍历出来的每一个元素就是函数的对象
arr.forEach(function(ele){
// 函数的对象 + ()来调用函数,然后打印
ele();
})
apply( ) 和 call( )方法中的第一个参数为空或者null
// 定义一个函数
function f1(x,y) {
console.log("结果是:"+(x+y)+this);
return "10000";
}
// 调用定义好的函数,此时的f1相当于 window对象 在使用
f1(10,20); // 结果是:30[object Window]
// apply和call方法也是函数的调用的方式
// apply和call方法中如果没有传入参数,或者是传入的是null,那么调用该方法的函数对象中的this就是默认的window
f1.apply(); // 结果是:NaN[object Window] 因为x和y都没有赋值
f1.call(); // 结果是:NaN[object Window]
f1.apply(null); // 结果是:NaN[object Window]
f1.call(null); // 结果是:NaN[object Window]
// apply和call都可以让函数或者方法来调用,传入参数和函数自己调用的写法不一样,但是效果是一样的
f1.apply(null,[100,200]); // 结果是:300[object Window] 相当于给定义的函数传入了参数x和y
f1.call(null,100,200); // 结果是:300[object Window]
var result1 = f1.apply(null,[10,20]);
var result2 = f1.call(null,10,20);
console.log(result1); // 10000
console.log(result2); // 10000
第一个参数不是 NULL
// 定义一个函数,谁调用这个函数,那么this就是谁
function f2(x,y) {
console.log("这个函数是window对象的一个方法:"+(x+y)+this.sex);
}
// window是顶级对象
// window.sex = "222"; 如果加上这一行代码,顶级对象window就有了sex属性,下面的打印就不会有 undefined 了
window.f2(10,20); // 这个函数是window对象的一个方法:30undefined 因为顶级对象window并没有 sex属性
// obj是一个对象,我们通过字面量自己创建的
var obj = {
age:10,
sex:"男"
};
// apply()方法中传入了obj我们自定义的obj对象,所以 this.sex 中的sex就是obj对象的sex属性
window.f2.apply(obj,[10,20]); // 这个函数是window对象的一个方法:30男
window.f2.call(obj,10,20); // 这个函数是window对象的一个方法:30男
apply和call可以改变this的指向
function Person(age,sex) {
this.age=age;
this.sex=sex;
}
// 通过原型添加方法
Person.prototype.sayHi=function (x,y) {
console.log("您好啊:"+this.sex);
return 1000;
};
var per=new Person(10,"男");
// 此时调用sayHi()方法的是 per实例对象,而per实例对象中确实有 sex的属性
per.sayHi(); // 您好啊:男
function Student(name,sex) {
this.name=name;
this.sex=sex;
}
var stu = new Student("小明","人妖");
// 改变了this的指向,指向了 Student(),也就相当于是Student()在调用Person()的sayHi()方法了
var r1 = per.sayHi.apply(stu,[10,20]); // 您好啊:人妖
var r2 = per.sayHi.call(stu,10,20); // 您好啊:人妖
console.log(r1); // 1000
console.log(r2); // 1000
function fA(x,y) {
console.log((x+y)+":===>"+this);
return "这是函数的返回值";
}
// apply和call调用
// 此时f1中的this是window
var r1=fA.apply(null,[1,2]); // 3:===>[object Window]
console.log(r1); // 这是函数的返回值
// 此时f1中的this是window
var r2=fA.call(null,1,2); // 3:===>[object Window]
console.log(r2); // 这是函数的返回值
// 改变this的指向
var obj = {
sex:"男"
};
// 本来f1函数是window对象的,但是传入obj之后,f1函数此时就是obj对象的
// 此时f1中的this是obj
var r3=fA.apply(obj,[1,2]); // 3:===>[object Object]
console.log(r3); // 这是函数的返回值
// 此时f1中的this是obj
var r4=fA.call(obj,1,2); // 3:===>[object Object]
console.log(r4); // 这是函数的返回值
function f1() {
console.log(this+":====>调用了");
}
// f1是函数,f1也是对象
console.dir(f1);
// 对象调用方法,说明,该对象中有这个方法
f1.apply(); // [object Window]:====>调用了
f1.call(); // [object Window]:====>调用了
console.log(f1.__proto__==Function.prototype); // true
// 所有的函数都是Function的实例对象
console.log(Function.prototype);//ƒ () { [native code] }
console.dir(Function);
// apply和call方法实际上并不在函数这个实例对象中,而是在Function的prototype中
函数名字.bind(对象,参数1,参数2,...);---->返回值是复制之后的这个函数 方法名字.bind(对象,参数1,参数2,...);---->返回值是复制之后的这个方法
// 定义一个函数
function f1(x, y) {
console.log((x + y) + ":=====>" + this.age);
}
var ff = f1.bind(null);
ff(10,20); // 30:=====>undefined
function f1(x, y) {
console.log((x + y) + ":=====>" + this.age);
}
function Person() {
this.age = 1000;
}
Person.prototype.eat = function () {
console.log("这个是吃");
};
var per = new Person();
// bind()方法把f1对象赋值给ff对象,同时在赋值的时候改变了this的指向,使this指向了 Person()对象
// 以下的两种方式本质上是一样的
var ff = f1.bind(per, 10, 20);
ff(); // 30:=====>1000
var ff = f1.bind(per);
ff(10,20); // 30:=====>1000
function PersonA(name){
this.name = name;
}
PersonA.prototype.eat = function(){
console.log(this+"====>"+this.name);
}
function StudentA(name){
this.name = name;
}
var obj1 = new PersonA("贾铭威");
var obj2 = new StudentA("贾飞天");
// bind()在赋值给ff的时候,改变了obj1的地址,指向了obj2 也就是 StudentA 所以打印的是 贾飞天 而不是 贾铭威
var ff = obj1.eat.bind(obj2);
ff(); // [object Object]====>贾飞天
bind( )方法的案例
function ShowRandom(){
// 1-10的随机数
this.num = parseInt(Math.random()*10 + 1);
}
// 添加原型方法
ShowRandom.prototype.show1 = function(){
// 改变了定时器中的this的指向了,本来应该是window,现在是实例对象了
window.setInterval(this.show2.bind(this),1000);
}
// 打印产生的随机数字
ShowRandom.prototype.show2 = function(){
console.log(this.num);
}
var sr = new ShowRandom();
sr.show1();
function f1(x,y) {
//函数中有一个name属性----->函数的名字,name属性是只读的,不能修改
console.log(f1.name); // f1
//函数中有一个arguments属性--->实参的个数
console.log(f1.arguments.length); // 2
//函数中有一个length属性---->函数定义的时候形参的个数
console.log(f1.length); // 2
//函数中有一个caller属性---->调用(f1函数在f2函数中调用的,所以,此时调用者就是f2)
console.log(f1.caller); // 调用者
}
function f2() {
console.log("f2函数的代码");
f1(1,2);
}
f2();
function f1(fn){
console.log("函数名为f1的函数执行了");
// 在函数内部调用外部传来的函数
// 如果是命名函数的话,函数名称 + () 相当于是在调用
fn();
}
// 匿名函数作为参数传入 f1()当中调用
f1(function(){
console.log("我是一个匿名函数");
});
// 定义一个命名函数
function f2(){
console.log("函数名为f2的函数执行了")
}
// 命名函数当做参数传入函数内部,并且想要在另一个函数内部调用的时候,不能加()
f1(f2);
function f3(func){
setInterval(function(){
console.log("定时器开始");
// 调用外部传入来的函数,外部传来的函数在函数的内部调用
func();
console.log("定时器结束");
},1000);
};
f3(f4);
function f4(){
console.log("我不想学习,可是没有办法");
}
function f1(){
console.log("f1函数开始");
return function(){
console.log("我是作为返回值使用的函数");
}
};
// f1()函数的返回值是 一个函数的对象
var func = f1();
func();
var obj = {};
console.log(obj instanceof Object); // true
// 获取某个对象的数据类型的样子
console.log(Object.prototype.toString.call(obj)); // [object Object]
var arr = [];
console.log(Object.prototype.toString.call(arr)); // [object Array]
console.log(Object.prototype.toString.call(new Date())); // [object Date]
//判断这个对象和传入的类型是不是同一个类型
function getFunc(type) {
return function (obj) {
return Object.prototype.toString.call(obj) === type;
}
}
var ff = getFunc("[object Array]");
var result = ff([10, 20, 30]);
console.log(result); // true
var ff1 = getFunc("[object Object]");
var dt = new Date();
var result1 = ff1(dt);
console.log(result1); // false
//三部电影,电影有名字,大小,上映时间
function File(name, size, time) {
this.name = name; // 电影名字
this.size = size; // 电影大小
this.time = time; // 电影的上映时间
}
var f1 = new File("jack.avi", "400M", "1997-12-12");
var f2 = new File("tom.avi", "200M", "2017-12-12");
var f3 = new File("xiaosu.avi", "800M", "2010-12-12");
// 把生成的对象放入数组里面去
var arr = [f2, f3, f1];
// 构建一个对排序的函数
// 传入的参数是 排序的依据
function fn(attr){
return function getSort(obj1,obj2){
if(obj1[attr] > obj2[attr]){
return 1;
}else if(obj1[attr] == obj2[attr]){
return 0;
}else{
return -1;
}
}
}
var ff = fn("name");
// 函数作为参数,对对象数组进行排序
arr.sort(ff);
for (var i = 0; i < arr.length; i++) {
console.log(arr[i].name + "====>" + arr[i].size + "===>" + arr[i].time);
}
作用域:变量可以起作用的范围
不使用var声明的变量是全局变量,不推荐使用。 变量退出作用域之后会销毁,全局变量关闭网页或浏览器才会销毁
- 函数允许访问函数外的数据.
- 整个代码结构中只有函数可以限定作用域.
- 作用域规则首先使用提升规则分析
- 如果当前作用规则中有名字了, 就不考虑外面的名字
隐式全局变量
function fn1() {
// 由于没有给变量名字加上 var 所以虽然这个变量定义在函数内部,但是在函数外部也可以访问到
// 所以也叫做隐式全局变量
num1 = 100;
console.log("函数内部"+num1);
}
fn1(); // 函数内部100
console.log("函数外部"+num1); // 函数外部100
用var声明的变量无法删除
var num2 = 200;
num3 = 300;
// 用var声明的变量无法删除
delete num2;
// 没有被var修饰的变量被从内存中删除了
delete num3;
console.log(typeof num2); // number
console.log(num2 + 10); // 210
console.log(typeof num3); // undefined
隐式全局变量和局部变量重名
num = 5;
function fn2(){
var num = 100;
num +=10;
console.log(num);
}
// 局部变量和隐式全局变量重名,在函数内部优先使用局部变量
fn2(); // 110
// 打印了全局变量
console.log(num); // 5
块级作用域
{
var num = 1000;
console.log(num); // 1000
}
if(true){
var num=10.2111;
}
console.log(num); // 10.2111
预解析过程:
- 把变量的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值。
- 把函数的声明提升到当前作用域的最前面,只会提升声明,不会提升调用。
- 先提升var,在提升function
预解析的练习 1
// 全局变量a
var a = 99;
// f是函数,虽然定义在调用的后面,但是函数声明会提升到作用域的顶部。
f();
console.log(a); // a=>99, 此时是全局变量的a
// 定义的函数
function f() {
// 当前的a变量是下面变量a声明提升后,默认值undefined
console.log(a); // undefined
// 局部变量 a
var a = 10;
console.log(a); // a => 10
}
// 输出结果:
undefined
10
99
预解析的练习 2
var num = 20;
fun(); // undefined
function fun() {
// var num 被提升到此处,但是值并没有被提升,所以值是 undefined
console.log(num);
var num = 201;
}
预解析的练习 3
// 全局变量 a
var a = 18;
f1();
function f1() {
// 局部变量 b
var b = 9;
// a被提升到此处,但是值不会带过来,所以是 undefined
console.log(a); // undefined
// 由于变量的定义和赋值都在打印变量之前,所以b的值为 9
console.log(b); // 9
var a = '123';
}
预解析的练习4
var a;
a = 18;
f22();
function f22() {
var b;
var a;
b = 9;
console.log(a); //undefined
console.log(b); //9
a = '123';
}
/*
变量---->局部变量和全局变量,
作用域:就是变量的使用范围
局部作用域和全局作用域
js中没有块级作用域---一对括号中定义的变量,这个变量可以在大括号外面使用
函数中定义的变量是局部变量
*/
while(true){
var num=10;
break;
}
console.log(num); // 10
{
var num2=100;
}
console.log(num2); // 100
if(true){
var num3=1000;
}
console.log(num3); // 1000
function f1() {
//局部变量
var num=10;
}
console.log(num); // 10
var num=10; //作用域链 级别:0
var num2=20;
var str = "abc"
function f1() {
var num2=20;
function f2() {
var num3=30;
console.log(num); // 10 一直向上找,最后找到了 num 结果num为 10
}
f2();
}
f1();
// 预解析:就是在浏览器解析代码之前,把变量的声明和函数的声明提前(提升)到该作用域的最上面
// 变量的提升
console.log(num);
// 这个变量提升到打印语句之前
var num=100;
// 函数的声明被提前了
f1();
function f1() {
console.log("这个函数,执行了");
}
var f2;
f2=function () {
console.log("小杨好帅哦");
};
f2();
JavaScript中的对象其实就是生活中对象的一个抽象 JavaScript的对象是无序属性的集合。 其属性可以包含基本值、对象或函数。对象就是一组没有顺序的值。我们可以把JavaScript中的对象想象成键值对,其中值可以是数据和函数。 对象的行为和特征 特征---属性 行为---方法
- 通过字面量的方式创建
var obj = {
// 属性
name: 'zs',
age: 18,
sex: true,
// 方法
sayHi: function () {
console.log(this.name);
}
};
- new Object( )创建对象
var person = new Object();
// 属性
person.name = "贾铭威";
person.age = 25;
// 方法
person.play = function(){
console.log("玩电脑");
console.log("玩手机");
}
// 判断 person 是不是一个对象
console.log(person1 instanceof Object);
console.log(person instanceof Object);
- 工厂函数创建对象
// 工厂模式,把创建对象的方法封装到函数当中
function createObject(name,age){
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sayWold = function(){
console.log("我的名字叫"+this.name+"我今年"+this.age);
}
// 把创建好的对象返回
return obj;
}
// 调用工厂函数创建对象
var person = createObject("贾铭威",26);
person.sayWold();
- 自定义构造函数,构造函数名一定要大写
// 构造函数名大写,要求创建对象的时候传入参数
function Person(name,age){
// 属性
this.name = name;
this.age = age;
// 方法
this.sayHi = function(){
console.log("我的名字叫"+this.name+"我今年"+this.age);
}
}
var per = new Person("贾铭威",26);
per.sayHi();
// 调用属性的两种方法
per["name"] = "刘三姐";
console.log(per["name"]);
console.log(per.name);
// 调用方法的另一种方法
per["sayHi"]();
构造函数 ,是一种特殊的函数。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。
- 构造函数用于创建一类对象,首字母要大写。
- 构造函数要和new一起使用才有意义。
new在执行时会做四件事情
new会在内存中创建一个新的空对象
new 会让this指向这个新的对象
执行构造函数 目的:给这个新对象加属性和方法
new会返回这个新对象
JavaScript中的this指向问题,有时候会让人难以捉摸,随着学习的深入,我们可以逐渐了解 现在我们需要掌握函数内部的this几个特点
- 函数在定义的时候this是不确定的,只有在调用的时候才可以确定
- 一般函数直接执行,内部this指向全局window
- 函数作为一个对象的方法,被该对象所调用,那么this指向的是该对象
- 构造函数中的this其实是一个隐式对象,类似一个初始化的模型,所有方法和属性都挂载到了这个隐式对象身上,后续通过new关键字来调用,从而实现实例化
通过for...in语法可以遍历一个对象
var obj2 = {
name:"贾铭威1",
age:26,
method2:function(){
console.log(this.name+this.age)
}
}
obj2.method2();
// 遍历一个对象
for(var key in obj2){
console.log("属性名为:"+ key + ",属性值是:"+obj2[key]);
}
// 遍历的结果如下
属性名为:name,属性值是:贾铭威1
属性名为:age,属性值是:26
属性名为:method2,属性值是:function(){
console.log(this.name+this.age)
}
json格式的数据也是用for ... in 的格式来遍历
var json = {
"name": "小明",
"age": "10",
"sex": "男"
};
for(var key in json){
cosole.log(json[key])
}
function fun() {
this.name = 'mm';
}
var obj = new fun();
console.log(obj.name); // mm
// 因为删除了对象的属性,所以属性值会是 undefined
delete obj.name;
console.log(obj.name); // undefined
Math.PI // 圆周率 Math.random() // 生成一个0-1之间的随机数 Math.floor()/Math.ceil() // 向下取整/向上取整 Math.round() // 取整,四舍五入 Math.abs() // 绝对值 Math.max()/Math.min() // 求最大和最小值 Math.power()/Math.sqrt() // 求指数次幂/求平方根
// 取绝对值
console.log(Math.abs('-1')); // 1
console.log(Math.abs(-2)); // 2
console.log(Math.abs(null)); // ---> 0 重点
console.log(Math.abs("string")); // NaN 因为字符串根本不会有绝对值的
// 向上取整
console.log(Math.ceil(12.3)); // 13
console.log(Math.ceil(12.9)); // 13
console.log(Math.ceil(12.09)); // 13
console.log(Math.ceil(12.03)); // 13
console.log(Math.ceil(12.92)); // 13
// 向下取整
console.log(Math.floor(12.3)); // 12
console.log(Math.floor(12.9)); // 12
console.log(Math.floor(12.09)); // 12
console.log(Math.floor(12.03)); // 12
console.log(Math.floor(12.92)); // 12
console.log(Math.fround(2)); // 2
console.log(Math.fround(2.1)); // 2.0999999046325684
console.log(Math.fround(2.9)); // 2.9000000953674316
// 找出一对数字的最大值
console.log(parseInt(Math.max(1,2,3,4,5,6,7,8,9))) // 9
// 最小值
console.log(parseInt(Math.min(1,2,3,4,5,6,7,8,9))) // 1
// 这种情况下生成的随机数都是小数
console.log(Math.random());
// 生成一个1-100之间的整数(包括100在内)
console.log(parseInt((Math.random()*100)+1));
找出参数的最大值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
function GetMaxNum(){
this.getMax = function(){
var maxNum = arguments[0];
// 遍历传入这个方法的所有参数
for(var i = 0;i < arguments.length;i++){
if(maxNum < arguments[i]){
maxNum = arguments[i];
}
}
// 返回参数的最大值
return maxNum;
};
}
var obj = new GetMaxNum();
console.log(obj.getMax(0.1,2,56));
</script>
</body>
</html>
随机产生一个16进制的颜色值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<style>
div {
width: 300px;
height: 200px;
background-color: pink;
}
</style>
<script>
// 随机产生一个16进制的颜色值
function getColor(){
var str = "#";
// 一个16进制的数组
var arr = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"];
for(var i = 0;i<6;i++){
var num = parseInt(Math.random()*16);
// 把产生的16进制的字符串进行拼接
str += arr[num];
}
return str;
}
// 页面记载的事件
window.onload = function(){
// 页面一加载就改变标签的背景颜色
document.getElementById("dv").style.backgroundColor = getColor();
console.log(getColor());
}
</script>
<body>
<div id="dv">
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
var date = new Date();
// 当前服务器所在时区的时间
console.log(date); // Mon Jul 22 2019 08:10:40 GMT+0800 (中国标准时间)
// 打印出传入的指定时间
var date1 = new Date("2018-08-02");
console.log(date1); // Thu Aug 02 2018 08:00:00 GMT+0800 (中国标准时间)
// 打印出传入的指定时间
var date3 = new Date("2017/08/12");
console.log(date3); // Sat Aug 12 2017 00:00:00 GMT+0800 (中国标准时间)
// 根据时间戳打印日期
var date4 = new Date(1563754652144);
console.log("时间戳转换为日期:" + date4);
// 时间戳转换为日期:Mon Jul 22 2019 08:17:32 GMT+0800 (中国标准时间)
// 获取日期的毫秒(时间戳)
// 1. HTML5中提供的方法,有兼容性问题
var time = Date.now();
console.log(time); // 1563754652144
// 2. 获取时间戳的方法
var time1 = new Date().valueOf();
console.log(time1); // 1563754809472
// 3. getTime() 返回毫秒数和valueOf()结果一样,valueOf()内部调用的getTime()
var time2 = new Date().getTime();
console.log(time2) // 1563754809472
// ------------------------------------------------
var dt = new Date();
// 获取年份
console.log(dt.getFullYear()); // 2019
// 获取月份 月份是从0月开始的,真实的月份要在前面加1
console.log(dt.getMonth() + 1); // 7
// 获取分钟
console.log(dt.getMinutes()); // 25 25分的意思
// 获取秒
console.log(dt.getSeconds()); // 53秒
// 获取星期
console.log(dt.getDay()); // 1 今天是周一
// 获取本月的第几天
console.log(dt.getDate()); // 22 今天是本月的第22天,今天是22号
// -------------------------------------------------------
var dt1 = new Date();
// 英文格式的日期
console.log(dt1.toDateString()); // Mon Jul 22 2019
// 数字格式的日期
console.log(dt1.toLocaleDateString()); // 2019/7/22
// 时分秒格式的日期
console.log(dt1.toTimeString()); // 08:40:51 GMT+0800 (中国标准时间)
// 把日期转换成字符串
console.log("字符串日期:" + dt1.toString());
var num = 100;
console.log(num.toString()); // 100
console.log(typeof num.toString()); // String
</script>
</body>
</html>
为了方便操作基本数据类型,JavaScript还提供了三个特殊的引用类型:String/Number/Boolean
// s1是基本类型,基本类型是没有方法的
var s1 = 'zhangsan';
var s2 = s1.substring(5);
// 当调用s1.substring(5)的时候,先把s1包装成String类型的临时对象,再调用substring方法,最后销毁临时对象, 相当于:
var s1 = new String('zhangsan');
var s2 = s1.substring(5);
s1 = null;
// 创建基本包装类型的对象
var num = 18; // 数值,基本类型
var num = Number('18'); // 类型转换
var num = new Number(18); // 基本包装类型,对象
// Number和Boolean基本包装类型基本不用,使用的话可能会引起歧义。例如:
var b1 = new Boolean(false);
var b2 = b1 && true; // 结果是什么
var str = "hello_world";
// 字符串特性:不可变性,字符串的值是不能改变.有的字符串看起来改变了,是指向改变了,不是值改变了
str[1] = "3";
console.log(str[1]); // e
console.log(str); // hello_world
// 字符串的遍历,字符串可以看成由字符组成的数组,js中没有字符类型,java中有字符类型,用单引号包裹起来
var str1 = "hello";
for(var i = 0;i<str1.length;i++){
console.log(str[i]); // h e l l o
}
// 实例方法---->必须要通过new的方式创建的对象(实例对象)来调用的方法
// 静态方法---->直接通过大写的构造函数的名字调用的方法(直接通过大写的对象名字调用的)
length------>字符串的长度
var str = "12345";
console.log(str.length); // 5
charAt(索引),返回值是指定索引位置的字符串
var str1 = "wlejlwemtw";
console.log(str1.charAt(5)); // w
// 超出索引,结果是空字符串
console.log(str1.charAt(100)); // 空的字符串
console.log(typeof str1.charAt(100)); // String
fromCharCode(数字值,可以是多个参数,用逗号分隔开来),返回的是ASCII码对应的值
var str = String.fromCharCode(107,108,109);
console.log(str); // klm
var str=String.fromCharCode(83,79,83);
console.log(str); // SOS
concat(字符串1,字符串2,...);返回的是拼接之后的新的字符串
var str2 = "我叫";
console.log(str2.concat("贾","铭","威。")); // 我叫贾铭威。
indexOf(要找的字符串,从某个位置开始的索引);返回的是这个字符串的索引值,没找到则返回-1
var str3 = "今天天气很不错,我们出去散步吧";
// 从索引3的位置开始找"天"这个字符串,找不到所以返回-1
console.log(str3.indexOf("天",3)); // -1
console.log(str3.indexOf("天",1)); // 1
console.log(str3.indexOf("天",2)); // 2
lastIndexOf(要找的字符串);从后向前找相应的字符串,但是索引仍然是从左向右的方式来显示,找不到则返回-1
var str3 = "今天天气很不错,我们出去散步吧";
console.log(str3.lastIndexOf("我")); // 8
replace("原来的字符串","新的字符串");用来替换字符串的
var str = "贾飞天你好";
// 只要字符串中存在 "你好" 这字符串的话,就进行替换
if(str.indexOf("你好") != -1){
str = str.replace("你好","我好啊啊啊啊啊");
} else{
alert("不存在");
}
console.log(str); // 贾飞天我好啊啊啊啊啊
slice(开始的索引,结束的索引);
// 从索引5的位置开始提取,到索引为10的前一个结束,没有10,并返回这个提取后的字符串
var str = "如果有一天我邪恶了,请记住,我曾纯洁过";
// 末尾超过了字符串的长度,直接截取到字符串的最后一位
str = str.slice(5,100);
console.log(str); // 我邪恶了,请记住,我曾纯洁过
str = str.slice(5,8);
console.log(str); // 请记住
console.log(str.slice(5,-3)); // 空字符串
split("要干掉的字符串",切割后留下的个数);切割字符串
var str="乔峰|慕容|凤姐|梅超风|小苏|大蛇丸";
// 通过"|"进行剪切
str = str.split("|");
console.log(typeof str); // Object
console.log(str); // ["乔峰", "慕容", "凤姐", "梅超风", "小苏", "大蛇丸"]
for(var i = 0;i<str.length;i++){
console.log(str[i]);
}
substr(开始的位置,个数);返回的是截取后的新的字符串
var str="qwelkfhweklgnl";
// 从索引为5的地方截取3个字符串,索引为5的字符串为 f
str=str.substr(5,3);
console.log(str); // fhw
substring(开始的索引,结束的索引),返回截取后的字符串,不包含结束的索引的字符串
var str="qwelkfhweklgnl";
// 索引为6的字符串为 h ,因此截取不到h,截取到h前面的f
console.log(str.substring(3,6)); // lkf
var str = "wlker";
// toUpperCase();转大写
console.log(str.toUpperCase()); // WLKER
// toLocaleUpperCase()转大写
console.log(str.toLocaleUpperCase()); // WLKER
var str = "LWJHW";
// toLowerCase();转小写
console.log(str.toLowerCase()); // lwjhw
// toLocaleLowerCase(); 转小写
console.log(str.toLocaleLowerCase()); // lwjhw
trim();干掉字符串两端的空格
var str = " wert"
console.log(str.trim()); // wert
var str = " wert "
console.log(str.trim()); // wert
prototype存在的必要性
function Person(name,age) {
this.name=name;
this.age=age;
this.eat=function () {
console.log("今天吃红烧土豆");
}
}
var per1=new Person("小白",20);
var per2=new Person("小黑",30);
console.dir(per1);
console.dir(per2);
// eat()方法的作用和内容都是一样的,但是因为创建了两个对象
// 两个对象创建了相同的方法,造成了效率浪费和内存浪费
console.log(per1.eat == per2.eat); // false 因为连个eat()方法用的是不同的内存地址,所是 false
// 把可能造成效率浪费的方法给单独抽取出来
function Eat(){
console.log("我正在吃面包");
}
// 重现构建对象,对象引用我们单独抽取出来的方法
function Student(name,age) {
this.name=name;
this.age=age;
this.eat=Eat;
}
var stu1 = new Student("贾飞天",20);
var stu2 = new Student("机密狗",26);
// console.dir() 显示一个对象的所有属性和方法
console.dir(stu1);
// 因为方法用的是一个相同的内存地址
// 数据共享,节约内存空间
console.log(stu1.Eat == stu2.Eat); // true 这样两个Eat()方法的内存地址就相同了,避免了资源的浪费
Javascript 规定,每一个构造函数都有一个 prototype
属性,指向另一个对象。
这个对象的所有属性和方法,都会被构造函数的实例继承。
这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在 prototype
对象上。
- 任何函数都具有一个
prototype
属性,该属性是一个对象 - 构造函数的
prototype
对象默认都有一个constructor
属性,指向prototype
对象所在函数 - 通过构造函数得到的实例对象内部会包含一个指向构造函数的
prototype
对象的指针__proto__
- 所有实例都直接或间接继承了原型对象的成员
// 构建一个对象
function Person(name,age) {
this.name=name;
this.age=age;
}
//通过原型来添加方法,解决数据共享,节省内存空间
Person.prototype.eat = function(){
console.log("我正在吃西瓜");
}
var p1=new Person("小明",20);
var p2=new Person("小红",30);
// 两个对象的方法相同,节约了内存空间
console.log(p1.eat==p2.eat); // true
console.dir(p1);
console.dir(p2);
实例对象中有__proto__
这个属性,叫原型,也是一个对象,这个属性是给浏览器使用,
不是标准的属性----->__proto__
----->可以叫原型对象
构造函数中有prototype这个属性,叫原型,也是一个对象,这个属性是给程序员使用,是标准的属性------>
prototype--->可以叫原型对象
实例对象的__proto__
和构造函数中的prototype相等--->true
又因为实例对象是通过构造函数来创建的,构造函数中有原型对象prototype
实例对象的__proto__
指向了构造函数的原型对象prototype
function Person(name,age) {
this.name=name;
this.age=age;
}
// 通过原型给添加方法
Person.prototype.play = function(){
console.log("捆绑play");
}
var p1=new Person("小明",20);
var p2=new Person("小红",30);
p1.__proto__.play(); // 捆绑play
// 实例对象的__proto__属性和构造函数中的prototype属性相等
// 因为实例对象的__proto__属性就是引用了构造函数中的prototype属性
console.log(p1.__proto__ == Person.prototype); // true
什么样子的数据是需要写在原型中? 需要共享的数据就可以写原型中 原型的作用之一:数据共享 属性需要共享,方法也需要共享 不需要共享的数据写在构造函数中,需要共享的数据写在原型中
// 因为每一个学生的姓名,年龄,性别都是特有的,因此不需要共享
function Student(name,age,sex) {
this.name=name;
this.age=age;
this.sex=sex;
}
// 所有学生的身高都是188,所有人的体重都是55
// 所有学生都要每天写500行代码
// 所有学生每天都要吃一个10斤的西瓜
Student.prototype.height = "188";
Student.prototype.weight = "55KG";
Student.prototype.study = function(){
console.log("学习");
}
Student.prototype.play = function(){
console.log("玩电脑")
}
// 实例化学生对象
var stu = new Student("贾铭威",57,"女")
console.dir(Student);
console.dir(stu);
function Student(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
// 使用原型添加方法 如果用这种方式添加的话,一定要加上构造器
Student.prototype = {
// 我们为了保持 constructor 的指向正确,建议手动加上构造器的指向
constructor:Student,
height:"173",
weight:"55KG",
eat:function(){
console.log("是东西就吃");
},
drink:function(){
console.log("是东西就喝");
}
}
var obj = new Student("贾铭威",26,"男");
obj.eat();
obj.drink();
原型中的方法可以互相访问
function Student(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
this.eat = function(){
console.log("没使用原型,吃东西");
// 实例对象的方法是可以互相调用的
this.drink();
};
this.drink = function(){
console.log("没使用原型,喝东西");
}
}
var stu1 = new Student("贾铭威",25,"男");
stu1.eat();
// 使用了原型
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
Person.prototype = {
eat:function(){
console.log("使用了原型,吃");
// 原型中的方法也是可以相互调用的
this.drink();
},
drink:function(){
console.log("使用了原型,喝");
}
}
var per1 = new Person("贾铭威");
per1.eat();
原型中的方法和属性跟实例方法的方法和属性同名
function Person(age,sex) {
this.age = age;//年龄
this.sex = sex;
this.eat = function () {
console.log("构造函数中的吃");
};
}
// 原型中的属性和构造方法中的属性以及构造方法重名
Person.prototype = {
sex:"男",
eat:function(){
console.log("在原型中的吃");
}
}
// 实例对象使用的属性或者方法,先在实例中查找,
// 找到了则直接使用,找不到则,去实例对象的__proto__指向的原型对象prototype中找,找到了则使用,找不到则报错
var per1 = new Person("贾铭威","女",20);
console.log(per1.sex); // 女
per1.eat(); // 构造函数中的吃
所有函数都有 prototype 属性对象。
我们可以给JS原生的对象添加我们自定义功能
// 为字符串添加倒排序的方法
String.prototype.mysort = function(){
var arr = [];
for(var i=this.length - 1;i >= 0;i--){
// 把倒叙遍历的数据添加到数组里面去
arr.push(this[i]);
}
var str = arr.toString();
console.log(str); // 5,4,3,2,1
}
var str = "12345";
str.mysort();
// 为Array内置对象的原型对象中添加方法 冒泡排序
Array.prototype.myReverse = function(){
for(var i = 0;i<this.length;i++){
for(var j = 0;j<this.length - 1 - i;j++){
// 冒泡排序之倒排序
if(this[j] > this[j+1]){
var temp = this[j];
this[j] = this[j+1];
this[j+1] = temp;
}
}
}
}
// 定义一个数组
var arrs = [2,1,0,3,5,7,4];
arrs.myReverse();
console.log(arrs); // [0, 1, 2, 3, 4, 5, 7]
// 为字符串添加说话的方法
String.prototype.sayHello = function(){
console.log(this + " Hello World");
}
var str = "你好";
str.sayHello(); // 你好 Hello World
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.map{
width: 800px;
height: 600px;
background-color: #CCC;
position: relative;
}
</style>
<div class="map" id="dv">
</div>
<script src="common.js"></script>
<script>
// 定义一个产生随机数对象的函数
(function(win){
// 创建一个随机数函数
function Random(){
// 随机数函数中只有方法,没有属性
}
// 给函数添加原型
Random.prototype.createRanNum = function(max,min){
return Math.floor(Math.random()*(max-min) + min);
}
// 把实例对象传递给全局
win.Random = new Random();
})(window);
// 产生小方块的对象
(function(window){
// 根据id来获取方块面板
var map = document.getElementById("dv");
// 构造小方块的函数
function Food(width,height,color){
// 先构建好小方块必要的属性
// 小方块的宽 加上一个默认值
this.width = width || 20;
// 小方块的高
this.height = height || 20;
// 小方块的横纵坐标
this.x = 0;
this.y = 0;
// 小方块的背景颜色
this.color = color;
// 构建小方块的元素 创建一个方块的标签
this.element = document.createElement("div");
};
// 在背景墙上初始化显示小方块的位置
Food.prototype.init = function(map){
// 设置小方块的样式
var div = this.element;
// 脱离文档流
div.style.position = "absolute";
div.style.width = this.width + "px";
div.style.height = this.height + "px";
div.style.backgroundColor = this.color;
// 把小方块添加到背景板中去
map.appendChild(div);
// 调用小方块的位置函数使小方块的位置随机
// 位置默认是 0,0的位置
this.render(map);
};
// 小方块出现的随机的位置
Food.prototype.render = function(map){
// 在背景版的范围之内,随机产生小方块出现的纵横坐标
var x = Random.createRanNum(map.offsetWidth/this.width,0)*this.width;
var y = Random.createRanNum(map.offsetHeight/this.height,0)*this.height;
// 把坐标赋值给小方块的属性
this.x = x;
this.y = y;
// 把其他属性也给赋值到小方块中
var div = this.element;
div.style.left = this.x + "px";
div.style.top = this.y + "px";
};
// 实例化对象,这样页面一加载,小方块就会被创建出来
// 每次的位置还不固定
var fd = new Food(20,20,"green");
fd.init(map);
})(window);
</script>
</head>
<body>
</body>
</html>
实例对象的原型
__proto__
和构造函数的原型prototype指向是相同的, 实例对象中的__proto__
原型指向的是构造函数中的原型prototype 实例对象中__proto__
是原型,浏览器使用的 构造函数中的prototype是原型,程序员使用的 原型链:是一种关系,实例对象和原型对象之间的关系---->是通过原型(__proto__
)来联系的
function Person(name,age){
// 构造函数中的this就是 实例对象
this.name = name;
this.age = age;
this.eat = function(){
console.log("人都要吃东西的");
}
}
Person.prototype.play = function(){
// 原型对象中方法中的this就是实例对象
this.size = "你猜";
console.log("人都要玩的");
}
var per = new Person("贾铭威",25);
// 实例对象的结构
console.dir(Person);
// 构造函数的结构
console.dir(per);
console.log(Person.prototype == per.__proto__); // true
原型指向可以改变 实例对象的原型
__proto__
指向的是该对象所在的构造函数的原型对象 prototype 构造函数的原型对象(prototype)指向如果改变了,实例对象的原型(__proto__
)指向也会发生改变 原型的指向是可以改变的 实例对象和原型对象之间的关系是通过__proto__
原型来联系起来的,这个关系就是原型链
function Student(name){
this.name = name;
};
Student.prototype.eat = function(){
console.log("Student + eat");
};
Student.prototype.sayHello = function(){
console.log("Student + sayHello");
};
function Person(name){
this.name = name;
};
Person.prototype.eat = function(){
console.log("Person + eat");
};
Person.prototype.sayHello = function(){
console.log("Person + sayHello");
};
// Studnet的原型指向了Person()的实例对象
Student.prototype = new Person();
// 虽然是用 Student() 创建出来的,但已经是Person的实例了
var obj = new Student();
obj.eat(); // Person + eat
obj.sayHello(); // Person + eat
实例对象中有
__proto__
原型 构造函数中有prototype原型 prototype是对象 所以,prototype这个对象中也有__proto__
,那么指向了哪里 实例对象中的__proto__
指向的是构造函数的prototype 所以,prototype这个对象中__proto__
指向的应该是某个构造函数的原型prototype Person的prototype中的__proto__
的指向 console.log(Person.prototype.__proto__
); per实例对象的__proto__
------->Person.prototype的__proto__
---->Object.prototype的__proto__
是null
function Person() {
}
Person.prototype.eat=function() {
console.log("吃东西");
};
var per = new Person();
console.dir(per);
console.dir(Person);
console.log(per.__proto__==Person.prototype); // true
console.log(per.__proto__.__proto__==Person.prototype.__proto__); // true
console.log(Person.prototype.__proto__==Object.prototype); // true 最终指向了Object
console.log(Object.prototype.__proto__); // null
// 人的构造函数
function Person(age) {
this.age=age;
}
// 人的原型中添加方法
Person.prototype.eat=function () {
console.log("人正在吃东西");
};
// 学生构造函数
function Student(sex) {
this.sex=sex;
}
// 学生的原型中添加方法---->先在原型中添加方法
Student.prototype.sayHi=function () {
console.log("您好哦");
};
// 改变了原型对象的指向,Student()指向Person()
Student.prototype = new Person(10);
var stu = new Student("男");
stu.eat(); // 人正在吃东西
// stu.sayHi(); // 无法打印,程序报错,因为指向了Person(),而Person没有sayHi()的方法
//人的构造函数
function PersonA(age) {
this.age=age;
}
//人的原型中添加方法
PersonA.prototype.eat=function () {
console.log("人正在吃东西");
};
//学生构造函数
function StudentA(sex) {
this.sex=sex;
}
//改变了原型对象的指向
StudentA.prototype = new PersonA(10);
//学生的原型中添加方法,方法是在改变指向之后添加的
StudentA.prototype.sayHi = function () {
console.log("您好哦");
};
var stuA=new StudentA("男");
stuA.eat(); // 人正在吃东西
// 改变指向之后添加的新的方法
stuA.sayHi(); // 您好哦
console.dir(stu);
var perA = new PersonA(10);
perA.eat(); // 人正在吃东西
// perA.sayHi(); // 程序报错,因为Person()没有sayHi()方法
当原型和实例对象的属性重名的时候,优先用实例对象的属性
实例对象属性找不到,才会去找原型的
function Person(age,sex) {
this.age=age;
this.sex=sex;
};
Person.prototype.age = "100";
Person.prototype.sex = "女";
// 实例属性和原型添加的属性重名了,会优先用原型的属性
var per = new Person(10,"男");
console.log(per.sex); // 男
console.log(per.age); // 10
// js是一门动态语言,对象名.属性的时候就已经有了这个属性了,但是属性没有给赋值,所以结果是 undefined
console.log(per.fdsfdsfsdfds); // undefined
JS不是一门面向对象的语言,JS是一门基于对象的语言,但是JS可以模拟面向对象的思想编程,
JS中会通过构造函数来模拟类的概念(class)
JS中的继承
首先继承是一种关系,类(class)与类之间的关系,JS中没有类,但是可以通过构造函数模拟类,然后通过原型来实现继承
继承也是为了数据共享,js中的继承也是为了实现数据共享
原型的作用
1.为了共享数据,节约空间
2.为了实现模拟类的继承
Js中的多态
多态:一个对象有不同的行为,或者是同一个行为针对不同的对象,产生不同的结果.
要想有多态,就要先有继承,js中可以模拟多态,但是不会去使用,也不会模拟
function Person(name,age,sex) {
this.name = name;
this.sex = sex;
this.age = age;
};
// 添加原型的方法
Person.prototype.eat = function () {
console.log("人可以吃东西");
};
Person.prototype.sleep = function () {
console.log("人在睡觉");
};
Person.prototype.play = function () {
console.log("生活就是不一样的玩法而已");
};
// 定义一个学生类
function Student(score) {
this.score = score;
};
// 改变学生类的指向,让学生类指向 Person()类,然后就可以继承Person类的属性和方法
Student.prototype = new Person("小明",10,"男");
// 添加一个新的原型方法
Student.prototype.study = function(){
console.log("学生都是要学习东西的");
}
// 创建一个学生的对象
var stu = new Student(100);
// 打印学生的属性
console.log(stu.score); // 100
// 继承来的Person的属性,虽然stu对象没有,但是可以使用父类的属性
console.log(stu.name); // 小明
console.log(stu.sex); // 男
console.log(stu.age); // 10
// 继承来的方法
stu.eat(); // 人可以吃东西
stu.play(); // 生活就是不一样的玩法而已
stu.sleep(); // 人在睡觉
// 学生自己的方法
stu.study(); // 学生都是要学习东西的
不适用构造函数继承的缺陷
function Person(name,age,sex,weight) {
this.name=name;
this.age=age;
this.sex=sex;
this.weight=weight;
}
Person.prototype.sayHi=function () {
console.log("您好");
};
// 创建Student对象
function Student(score) {
this.score=score;
}
// 希望人的类别中的数据可以共享给学生---继承
Student.prototype = new Person("小明",10,"男","50kg");
var stu1 = new Student("100");
// 1.因为继承了父类,所以也会把父类的属性和父类的属性值一同继承过来
console.log(stu1.name,stu1.age,stu1.sex,stu1.weight,stu1.score); // 小明 10 男 50kg 100
stu1.sayHi(); // 您好
var stu2 = new Student("120");
// 2.因为把父类的属性值一同继承了过来,要想有子类的独特的属性值的话,需要子类给继承过来的属性重新赋予子类的值
stu2.name = "张三";
stu2.age = 20;
stu2.sex = "女";
console.log(stu2.name,stu2.age,stu2.sex,stu2.weight,stu2.score); // 张三 20 女 50kg 120
stu2.sayHi(); // 您好
var stu3 = new Student("130");
// 3.如果不重新赋值的话,得到的还是父类的属性值
console.log(stu3.name,stu3.age,stu3.sex,stu3.weight,stu3.score); // 小明 10 男 50kg 130
stu3.sayHi(); // 您好
继承的时候,不用改变原型的指向,直接调用父级的构造函数的方式来为属性赋值就可以了---> 借用要继承的父类的构造函数
格式:--->借用构造函数:构造函数名字.call(当前对象,属性,属性,属性....);
有点:--->解决了属性继承,并且值不重复的问题
==缺陷==:--->==父级类别中的方法不能继承==
function PersonA(name, age, sex, weight) {
this.name = name;
this.age = age;
this.sex = sex;
this.weight = weight;
}
PersonA.prototype.sayHi = function () {
console.log("您好");
};
function StudentA(name,age,sex,weight,score) {
// 借用要继承的父类的构造函数
PersonA.call(this,name,age,sex,weight);
// this指代当前对象,可以通过this来为子类对象添加新的属性
this.score = score;
};
var stu1 = new StudentA("小明",10,"男","10kg","100");
console.log(stu1.name, stu1.age, stu1.sex, stu1.weight, stu1.score); // 小明 10 男 10kg 100
// stu1.sayHi(); 程序会报错,因为只是借用了父类的构造方法,父类的方法并没有被继承
var stu2 = new StudentA("小红",20,"女","20kg","120");
console.log(stu2.name, stu2.age, stu2.sex, stu2.weight, stu2.score); // 小红 20 女 20kg 120
var stu3 = new StudentA("小丽",30,"妖","30kg","130");
console.log(stu3.name, stu3.age, stu3.sex, stu3.weight, stu3.score); // 小丽 30 妖 30kg 130
原型实现继承方法
借用构造函数实现继承属性
组合继承:原型继承+借用构造函数继承 把父类的属性和方法完整的继承
function Person(name,age,sex) {
this.name=name;
this.age=age;
this.sex=sex;
}
Person.prototype.sayHi = function () {
console.log("父类的sayHi");
};
function Student(name,age,sex,score){
// 借用父类的构造方法
Person.call(this,name,age,sex);
// 添加子类自己的属性
this.score = score;
}
Student.prototype.A = function(){
console.log("继承之前的A方法")
}
// Studnet()指向Person()实现继承
Student.prototype = new Person();
Student.prototype.A = function(){
console.log("继承之后的A方法")
}
Student.prototype.eat=function () {
console.log("吃东西");
};
var stu = new Student("小黑",20,"男","100分");
console.log(stu.name,stu.age,stu.sex,stu.score); // 小黑 20 男 100分
stu.sayHi(); // 父类的sayHi
stu.eat(); // 吃东西
stu.A(); // 继承之后的A方法 相当于重写了继承之前的A方法
var stu2 = new Student("小黑黑",200,"男人","1010分");
console.log(stu2.name,stu2.age,stu2.sex,stu2.score); // 小黑黑 200 男人 1010分
stu2.sayHi(); // 父类的sayHi
stu2.eat(); // 吃东西
Person的构造中有原型prototype,prototype就是一个对象,那么里面的age,sex,height,play都是该对象中的属性或者方法
拷贝继承: 把一个对象中的属性或者方法直接复制到另一个对象中
var obj1={
name:"小糊涂",
age:20,
sleep:function () {
console.log("睡觉了");
}
};
// 拷贝继承
var obj2 = obj1;
console.log(obj2.age + obj2.name);
console.log(obj2.sleep());
var objA={
name:"小糊涂",
age:20,
sleep:function () {
console.log("睡觉了");
}
};
// 重新开辟了一份内存空间
var objB = {};
for(var key in objA){
objB[key] = objA[key];
}
console.log(objB.name); // 小糊涂
// Person的构造中有原型prototype,prototype就是一个对象,那么里面的age,sex,height,play都是该对象中的属性或者方法
// 拷贝继承;把一个对象中的属性或者方法直接复制到另一个对象中
function Person() {
}
Person.prototype.age=10;
Person.prototype.sex="男";
Person.prototype.height=100;
Person.prototype.play=function () {
console.log("玩的好开心");
};
// 重新开辟了一份内存空间
var BB = {};
for(var key in Person.prototype){
BB[key] = Person.prototype[key];
}
console.log(BB.sex); // 男
闭包的概念:函数A中,有一个函数B,函数B中可以访问函数A中定义的变量或者是数据,此时形成了闭包(这句话暂时不严谨)
闭包的模式:函数模式的闭包,对象模式的闭包
闭包的作用:缓存数据,延长作用域链
闭包的优点和缺点:缓存数据
// 函数模式的闭包:在一个函数中有一个函数
function f1() {
var num=10;
//函数的声明
function f2() {
console.log(num);
}
//在一个函数的里面定义了另一个函数,并且调用
f2();
}
f1(); // 10
// 对象模式的闭包:函数中有一个对象
function f3(){
var num = 10;
var obj = {
age:num
};
console.log(obj.age);
}
f3(); // 10
function f4() {
var num=10;
return function (){
console.log(num); // 10
return num;
}
}
// ff 是第一次return返回的对象
var ff = f4();
var result = ff();
console.log(result); // 10
function f5() {
var num = 100;
return {
age:num
}
}
var obj = f5();
console.log(obj.age); // 100
闭包案例 1
// 普通的函数
function fn1(){
var num = 10;
num++;
return num;
}
console.log(fn1());
// 函数模式的闭包
function fn2(){
var num = 20;
return function(){
num++;
return num;
}
}
var ff = fn2();
console.log(ff());
闭包案例 2
总结:如果想要缓存数据,就把这个数据放在外层的函数和里层的函数的中间位置 闭包的作用:缓存数据.优点也是缺陷,没有及时的释放 局部变量是在函数中,函数使用结束后,局部变量就会被自动的释放 闭包后,里面的局部变量的使用作用域链就会被延长
function fn1(){
var num = parseInt(Math.random()*10+1);
return function(){
console.log(num);
}
}
// 闭包的方式产生随机数
fn1()();
闭包案例 3
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>对自己狠点</title>
<style>
ul {
list-style-type: none;
}
li {
float: left;
margin-left: 10px;
}
img {
width: 200px;
height: 180px;
}
input {
margin-left: 30%;
}
</style>
</head>
<body>
<ul>
<li><img src="images/ly.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
<li><img src="images/lyml.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
<li><img src="images/fj.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
<li><img src="images/bd.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
</ul>
<script>
// 获取所有的input标签
var tagObjs = document.getElementsByTagName("input");
// 遍历标签,给标签添加点击事件
for(var i = 0;i<tagObjs.length;i++){
tagObjs[i].onclick = changeValue();
}
// 在点击事件中改变 value 的值
function changeValue(){
// 用来统计一共点赞的次数
var value = 2;
return function(){
this.value = "赞(" + (value++) + ")";
}
}
</script>
</body>
</html>
递归一定要有一个结束的条件
var i = 1;
function fn1(){
// 当i大于等于6的时候,递归就会停止
if(i < 6){
i++;
fn1();
}
console.log("我是一个递归函数,我执行了");
}
fn1();
function getSum(num){
if(num == 1){
return 1;
}
return num + getSum(num -1);
}
console.log(getSum(3));
浅拷贝:拷贝就是复制,就相当于把一个对象中的所有的内容,复制一份给另一个对象,直接复制,
或者说,就是把一个对象的地址给了另一个对象,他们指向相同,两个对象之间有共同的属性或者方法,皆可使用
var obj1 = {
age:10,
sex:"男",
car:["奔驰","宝马","特斯拉","奥拓"]
};
var obj = {};
// 遍历对象中的属性,然后拷贝给新对象
for (var key in obj1){
obj[key] = obj1[key];
}
console.log(obj);
深拷贝:拷贝还是复制,深:把一个对象中所有的属性或者方法,一个一个的找到.
并且在另一个对象中开辟相应的空间,一个一个的存储到另一个对象中
var obj1={
age:10,
sex:"男",
car:["奔驰","宝马","特斯拉","奥拓"],
dog:{
name:"大黄",
age:5,
color:"黑白色"
}
};
// 创建一个空对象
var obj2 = {};
function extend(a,b){
for(var key in a){
//获取a对象的每一个属性值
var item = a[key];
// 判断这个属性值是不是数组
if(item instanceof Array){
b[key] = [];
// 再一次的调用这个方法进行判断,判断通过就复制
extend(item,b[key]);
}else if(item instanceof Object){
// 给创建一个空的对象,等待复制
b[key] = {};
extend(item,b[key]);
}else{
// 如果是普通的数据的话,就直接复制到新的对象中去
b[key] = item;
}
}
}
// 调用复制的方法
extend(obj1,obj2);
console.dir(obj1);
console.dir(obj2);
创建正则表达式对象
1.通过构造函数创建对象
2.字面量的方式创建对象
// 创建正则表达式对象
var reg = new RegExp(/\d{5}/);
// 要进行匹配的字符串
var str = "我的电话是10086";
// 调用方法验证是否匹配正确
var flag = reg.test(str);
console.log(flag); // true
// 字面量的方式创建正则表达式
var reg = /\d{1,5}/;
var flag = reg.test("小苏的幸运数字:888");
console.log(flag); //true