我们向系统申请了一个地方,这个地方门牌号是我们定义好的属性名,然后把属性值赋予属性名。如果重复给同一属性名赋值,最后赋值的一个将成为最终的属性值。
a. 变量名必须以英文字母,_,或$开头
b. 变量名可以为英文字母、_、$或者数字
c. 不可以用系统的关键字和保留字作为变量名
Number String Boolean undefined null(null用来占位置的)
object array function
var a = 1:
如果英文写成了中文或者多写括号少些括号等低级错误,机器会在执行一行一行代码之前通篇扫描中检查出来,一行代码都不会被执行。
-
let a =
1;
-
document.
write(a);
-
document.
write(b);
-
document.
write(
20);
第一次通篇扫描扫不出来,在执行一行解释一行时,会发现b未定义,报错,20也不会被输出。
a. “+”
第一个作用是两个Number的数字相加
第二个作用是任何东西加字符串都等于字符串
b. - * / % = ()
-
// 特殊的除法
-
0/
0
-
// 结果为NaN
-
1/
0
-
// 结果为Infinity
-
0/
1
-
// 结果为0
-
-
- * / % = ()的优先级
-
()的优先级最高 = 的优先级最低
备注:Infinity不可能传给后端,Infinity要先变成字符串,再传给后端,后端进行解析
c. ++ -- += -= *= /= %=
-
let c = c +
1; 等价于
let c +=
1;
-
let a =
10;
-
let b = ++a -
1 + a++;
-
document.
write(b +
"" + a);
-
// 执行结果:b = 11 -1 + 11; a = 10 + 1 + 1 = 12
-
// ++a 是a先加 a++ 是a后加
-
// 小栗子
-
如果让
let a =
123, b =
456;中的a和b互换值
-
-
a = a + b;
-
b = a - b;
-
a = a - b;
> < == >= <= !=
-
// 小栗子
-
var a =
'a' >
'b';
-
document.
write(a)
// 输出为false
-
reason: 字符串比较的是
Ascll码,其中a的的ascll码 小于b的ascll
'a' =
"A" +
32 =
113 ;
'A' =
81
-
-
-
//根据上面的结论再来一个 小栗子
-
var a =
'10' >
'8'
-
document.
write(a)
// 输出为false
-
reason:先是
'1' 跟
'8'比较,小于,直接返回
false, 否则一直比下去返回值
-
-
// 特殊规则
-
a =
1 ==
1; 输出为
true
-
a =
undefined ==
undefined 输出为
true
-
a =
undefined ==
null 输出为
true
-
a =
Infinity ==
Infinity 输出为
true
-
a =
NaN ==
NaN 输出为
false == >
NaN不等于任何东西,一急眼自己都不认识
-
&& || !
-
&& ===> 会先看&&前面的表达式转换为
Boolean,如果只有两个表达式的话,第一个为
false,那么直接返回第一个值的结果,否则直接返回第二个值的结果。
-
// 碰到假就停下返回
-
// 小栗子 (&& 可以当做短路来判断逻辑)
-
var a =
1 &&
2 +
2; 输出的结果为
4
-
var a =
0 &&
2 +
2;输出的结果为
0
-
2 >
1 &&
document.
write(
'你真棒') 输出的结果是
'你真棒';
-
-
-
-
|| ===> 如果只有两个表达式的话,如果第一个值是
true, 那么直接返回第一个值的结果,否则返回第二个值的结果
-
// || 碰到真就返回
-
-
! ===> 取反 把这个东西转换为
Boolean取反再取反
-
// 小栗子
-
var data = {};
-
var a = !! data;
-
document.
write(a); 输出为
true
-
// if 和 && 的转换小栗子
-
if(
1 >
2) {
-
document.
write(
'a');
-
}
-
等价于
-
1 >
2 &&
document.
write(
'a');
-
// 根据浏览器端输入的值的不同执行不同的操作
-
let n =
parseInt(
window.
prompt(
'input'));
-
switch(n) {
-
case
'a':
-
console.
log(
'a');
-
break;
-
case
'b':
-
console.
log(
'b');
-
break;
-
case
'c':
-
console.
log(
'c');
-
}
-
// continue
-
for(
var a =
0; a <
100; a++) {
-
if(a %
7 ===
0 || a %
10 ===
7) {
-
continue;
-
}
-
console.
log(a);
-
}
-
// for循环中如果a逢7或是7的倍数那么跳出本次循环,输出其他的小于100的数字
-
-
// break
-
// 判断今天输入的date是星期几
-
let date =
parseInt(
window.
prompt(
"input"));
-
switch(date) {
-
case
"monday":
-
case
"tuesday":
-
case
"wednesday":
-
case
"thursday":
-
case
"friday":
-
console.
log(
"工作日");
-
break;
-
case
"monday":
-
case
"sunday":
-
console.
log(
"休息日");
-
}
-
-
// 上述程序如果没有break,会一直走完case "sunday"
-
// 如何方便地打印10个a
-
for(
var a =
0; a <
10; a++) {
-
document.
write(a)
-
}
-
// 原理分析
-
第一步
var a =
0;
-
第二步
if(a <
10) {
document.
write(a) }
-
第三步 a++;
-
第四步
if(a <
10) {
document.
write(a) }
-
第五步 a++;
-
第六步
if(a <
10) {
document.
write(a) }
-
... 直到 a =
10 退出循环 输出
1
2
3
4
5
6
7
8
9
-
-
// 以上的for循环还可以写成下面的
-
var a =
0;
-
for(;a <
10;) {
-
document.
write(a);
-
a++;
-
}
-
-
-
// 小测试
-
// 在for循环的()中只能写一句话,执行体只能写一句话,打印100个数
-
var a =
100;
-
for(; a-- ;) {
-
document.
write(a +
'');
-
}
-
输出
99 ~
0
-
// 如果for()里面只写中间部分,比如for(; a < 10 ;)和while循环是完全一样的
-
var a =
0;
-
for(;a <
10;) {
-
document.
write(a);
-
a++;
-
}
-
等于
-
var a =
0;
-
while(a <
10) {
-
document.
write(a);
-
a++;
-
}
-
-
// 小栗子
-
// 写一个逢7 或 7的倍数输出打印切小于100
-
var a =
100;
-
while(a %
10 ===
7 || a %
7 ===
0) {
-
document.
write(a);
-
a ++;
-
}
// do{} while() 循环,不管满足不满足条件,都会执行一次
-
var n =
parseInt(
window.
prompt(
'input'));
-
var mul =
1;
// 用来存储2的n次方
-
for(
var i =
0; i < n; i++) {
-
mul *=
2;
-
}
-
console.
log(mul)
-
// 在浏览器中输入10 输出 1024
-
var n =
parseInt(
window.
prompt(
"input"));
-
var mul =
1;
// 用来存储n!
-
// 因为1!是1,所以初始化i为1
-
for(
var i =
1; i <= n; i++) {
-
mul *= i;
-
}
-
console.
log(mul);
-
// 输入10 输出 3628800
-
for(
var i =
2; i <
100; i ++) {
-
var count =
0
-
for(
var j =
1; j <
Math.
sqrt(
100); j++) {
-
if(i % j ===
0) {
-
count ++;
-
}
-
}
-
if(count ===
2) {
-
console.
log(i)
-
}
-
count =
0;
-
}
-
// 数组里面可以存放很多东西,如 1, 'a', undefined, true,甚至可以是数组
-
// 小栗子 打印数组中的每一项
-
var arr = [
1,
2,
3,
undefined,
'abc'];
-
let len = arr.
length;
-
for(
let i =
0; i < len; i++) {
-
console.
log(arr[i])
-
}
-
输出
1
2
3
undefined abc
-
// 以前编程面向过程,后来出现了面向对象(按照人的思维方式来思考操作),对象也是存储数据的仓库,要比数组更加直观一点,对象中的每个属性值都要有属性名,属性名可以加双引号,可以不加,属性值可以放boolean/undefined/null等,还可以存放数组/对象
-
-
//小栗子 定义一个对象
-
var lei = {
-
lastName:
'lei',
-
age:
'23',
-
sex:
'man',
-
wife:
'baicai'
-
}
-
// 如何取对象的值
-
lei.
lastName ==> 输出 lei
-
-
// 如何赋值
-
lei.
firstName =
'zhou';
-
-
// 如何删除对象的属性
-
delete lei.
age 删除成功返回
true
-
1.面向过程
-
c就是面向过程,它会有机械的想法,第一步想要干嘛,第二步想要干嘛
-
// 比如 人想要飞
-
机械: 给人焊一个翅膀,从高处跳下就能飞
-
人: 不可能
-
2. 面向对象
-
人的编程思想
-
-
// java javascript c++ 面向对象语言,后来javascript 面向对象和面向过程
number string boolean undefined object(null array object ) function
-
// typeof 小栗子
-
var num =
123;
typeof(num)
// 输出 "number"
-
var num = [] | {} |
null;
typeof(num)
// 输出 "object"
-
var num =
undefined;
typeof(num)
// 输出 "undefined"
-
var num =
function(
) {};
typeof(num)
// 输出 "function"
a. typeof(xxx) 使用typeof(数据)
b. typeof xxx 中间加空格
不过最好使用a方案
-
// 笔试题
-
typeof(
typeof
undefined)
-
// 输出 "string"
用途: 看起来不像数字的转换得到NaN,Number目的是千方百计地把()中的值转换为数字
-
// 小栗子
-
var num =
Number(
'123'); 输出
123
-
下面是试了一下其他值的转换
-
true ==>
1
-
undefined ==>
NaN
-
null ===>
0
-
'a' ===>
NaN
-
'2' *
'1' ===>
2
-
'2' -
'1' ===>
1
-
'123abc' ===>
NaN
-
用途1: 把()中的值转换为整型,小数点的部分去掉,parseInt()的目的是把数字和字符串的数字(数字后面加字符串)转换为整型,
parseInt从数字位一直截到非数字位
-
// 小栗子
-
var a =
123.99;
-
parseInt(a);
-
// 输出 123
-
-
a =
'123abc';
-
parseInt(a);
-
// 输出 123
-
-
a =
'abc123';
-
parseInt(a);
-
// 输出 NaN
用途2: parseInt(number, radix(基底)), 以radix为基底进制的数字转换为10进制的数字
-
// 小栗子
-
var demo=
'10';
-
var num =
parseInt(demo,
16);
-
console.
log(
typeof(num) +
' : ' + num);
-
// 输出 number : 16
-
-
num后面的
16是
16进制,
16进制为
-
1
2
3
4
5
6
7
8
9 a b c d e f ==>
10
用途: 从数字位一直截到第一个小数点结束到第二个小数点,遇到非数字位直接返回前面的数字
-
// 小栗子
-
var num =
"123.444.555"
-
parseFloat(num);
-
输出
123.444
-
-
num =
'123.44abc';
-
parseFloat(num);
-
输出
123.44
-
-
num =
'abc44.123abc';
-
parseFloat(num);
-
输出
NaN
用途: 跟Number一样,致力于把()中的值转换为字符串
-
// 小栗子
-
var num =
123;
-
String(num);
-
输出
'123'
-
-
num =
undefined;
-
String(num);
-
输出
'undefined'
-
-
num =
null;
-
String(num);
-
输出
'null'
用途: 除了被认为是false的六个值, 0 '' false undefined null NaN, 其他都是true
-
// 小栗子
-
Boolean(
0 |
'' |
false |
undefined |
null |
NaN)
-
// 输出是false
用途1: radix如果不传值的话, 用途是将demo转换为数字类型
-
// 小栗子
-
var num =
123;
-
num.
toString();
-
输出
'123'
-
-
num =
undefined |
null;
-
num.
toString();
-
// 会报错 Uncaught TypeError: Cannot read property 'toString' of null
-
用途2: radix如果传值的话,是将demo转换为目标基底的值
-
// 小栗子
-
var demo =
123;
-
var num = demo.
toString(
10);
-
输出为
123
-
-
num = demo.
toString(
8);
-
-
// 有一个好玩的
-
// 先给你一个二进制的数,让你转换为16
-
var num =
1010101010;
-
var test =
parseInt(num,
2);
// 首先转换为10进制
-
var tostring = test.
toString(
16);
-
输出为
"2aa"
-
// 原理 isNaN里面首先会通过Number()转换,之后再与NaN进行比较
-
// 小栗子
-
isNaN(
'abc') 输出为
true
-
isNaN(
'123') 输出为
false
-
// 提前调用Number(),'abc'虽然转换成了NaN,但是它的typeof是Number
-
// 小栗子
-
var a =
'abc';
-
a++;
-
a--;
-
-a;
-
+a;
-
typeof(a); 输出为
"number"
-
// 原理: 加号两边如果有string,就会调用String(),把不是字符串的转化成字符串然后连接
-
var a =
'a' +
1; 输出为
'a1'
-
// 原理: 调用Number
-
// 小栗子
-
var a =
'1' *
'1';
-
console.
log(
typeof(a) +
" " + a);
-
输出 number
1
-
// 原理: 两个值比较,一方有数字,另外一方转换为数字
-
// 小栗子
-
var a =
'3' >
2;
-
console.
log(a);
-
输出 为
true
-
// 小栗子
-
var a =
1 ==
'1' 输出为
true
-
var a =
1 ==
true 输出为
true
-
undefined ==
null ==> 因为
undefined和
null既不大于
0,也不小于
0,所以相等
-
-
// 三等比较 从左到右比较
-
100 >
10 >
0 ? 输出为
true
-
首先
100 >
10 输出
true
-
接着
true >
0 ? 输出为
true
-
-
// 特殊需要记忆
-
NaN ==
NaN 输出
false 非数不等与自己,也不可能等于其他
-
-
-
// 小栗子
-
var a =
1 ===
'1' 输出为
false 因为
1 是number类型,
'1'是string类型
注意: 在没有定义a的前提现,输出a,会报 a is not defined, 当且仅当输出 typeof(a)的时候回输出undefined,不会报错
-
function
test(
) {}
-
关键字 函数名
-
// 函数跟数组一样,栈里面是指向堆的地址,堆里面是内容
-
// 无论是变量名还是函数名都要遵循"小头风的规则", 如下
-
function
theFirstName(
) {}
-
console.
log(theFirstName)
-
输出为
function
theFirstName(
) {}
-
-
// js不输出函数的地址,它输出地址指向那个房间的内容
-
// 命名函数表达从语义上来说是再给变量赋值函数的时候,该函数有函数名
-
// 小栗子
-
var test =
function
abc(
) {
-
console.
log(
'a')
-
}
-
-
// abc 是函数名,test()会执行,abc()不会执行
-
// 输出test.name 输出为abc
-
// 匿名函数表达式,从语义上来说是给变量赋值函数的时候,该函数没有函数名
-
// 小栗子
-
var test =
function(
) {
-
console.
log(
'a');
-
}
-
// test.name 是 test
-
// 函数组成形式
-
必须有
function 函数名 ()括号 {}花括号
-
// 形参或者实参(可有可无), 真正的编程,有参数才更有意义
-
// 小栗子
-
function
test(
a, b ) {
// 这里的a b是形参
-
console.
log(a + b);
-
}
-
test(
1,
2)
// 这里的1 2 是实参
-
输出为
3
注意: js参数最强大的有点就是不限制位数,实参和形参位数可以不对等,实参可以比形参多,反之也可以
-
// 形参比实参多
-
// 小栗子
-
function
sum(
a, b, c) {
-
console.
log(a + b + c)
-
}
-
sum(
1,
2)
-
输出是
NaN 因为
undefined 在
Number转换的时候变成了
NaN
-
以上栗子中形参是a b c, 实参是
1
2
-
-
//实参比形参多
-
function
sum(
a, b) {
-
console.
log(a + b);
-
}
-
sum(
1,
2,
3);
-
输出为
3
-
以上栗子中a b是形参,
1
2
3是实参
-
-
// 如何读取形参的长度呢
-
// 只需要 函数名.length即可获取
-
-
// 如何读取实参的长度呢
-
// 只需要 arguments.length
下面有一个小测试: js实现任意数求和,任意数的意思是不规定你传入几个数字
-
function
sum(
) {
-
var result =
0;
-
var len =
arguments.
length;
-
for(
let i =
0; i < len; i++) {
-
result +=
arguments[i]
-
}
-
console.
log(result);
-
}
-
sum(
1,
5,
6,
7);
-
输出为
19
-
-
// 测试 arguments[0] 和第一个实参是绑定关系吗
-
function
sum(
a, b) {
-
arguments[
0] =
1;
-
console.
log(a);
-
}
-
sum(
2,
3);
-
输出为
1
-
// 看来是绑定关系
-
-
// 有个特殊情况
-
function
sum(
a, b) {
-
arguments[
1] =
2;
-
console.
log(b);
-
}
-
sum(
1);
-
输出结果是
undefined
-
// 此时发现,只有形参和实参有对应关系的时候,他们才有映射关系
return 必须写在函数里,它有两个作用,第一个作用是返回一个值,第二个作用是终止函数
终止函数,如果没有写在function,系统会自动在函数的最后加一个return undefined;
-
var a =
123;
// 此时的a是全局变量
-
fucntion
test(
) {
-
var b =
345;
// 此时的b是局部变量
-
}
-
-
备注: 局部变量可以访问全局变量,全局变量不可以访问局部变量(里面可以访问外面的,外面的不能访问里面的)
好处: 代码简洁 但是效率不高
-
// 小栗子
-
// 写n!(递归) 已知1! === 1
-
// 分析 n! 为 n * (n - 1) * (n -2) * 一直乘到 1
-
var n =
window.
prompt(
"input");
-
function
mul(
n) {
-
if(n ===
1) {
-
return
1;
-
}
-
return n *
mul(n-
1);
-
}
-
mul(n);
-
// 小栗子
-
// 写一个函数 实现斐波那契数列
-
// 找规律 fb(n) = fb(n - 1) + fb(n - 2)
-
function
fb(
n) {
-
if(n ==
1 || n ==
2) {
-
return
1;
-
}
-
return
fb(n -
1) +
fb(n -
2);
-
}
-
fb(
3);
-
// 输出: 3
系统会通篇扫描一遍,看程序有没有低级错误(少些")" "}"),如果发现有低级错误,系统将停止执行
函数声明整体提升,变量 声明提升
imply global 暗示全局变量:任何变量,如果未经声明就赋值,此变量就归全局对象所有。
-
// 小栗子
-
a =
10;
-
a 因为没有声明就进行赋值,相当于
-
window.
a =
10;
-
-
-
// 又一个小栗子
-
function
test () {
-
var a = b =
123;
-
}
-
test();
-
console.
log(b);
-
console.
log(a);
-
输出
123 a is not defined
函数预编译发生在函数将要执行的前一刻
1. 首先创建AO(Activation Object)
2.找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
3.将实参和形参相统一
4.在函数体里面找函数声明,将函数声明的函数名作为AO的另一个属性名,函数声明的函数体作为该属性名的属性值
-
// 小栗子
-
function
fn(
a) {
-
console.
log(a);
-
var a =
123;
-
console.
log(a);
-
function
a(
) {}
-
console.
log(a);
-
console.
log(b);
-
var b =
function(
) {};
-
console.
log(b);
-
function
d(
) {}
-
console.
log(d)
-
}
-
fn(
1);
-
输出
function(
){}
123
123
undefined
function(
) {}
function
d(
){}
1. 生成GO对象(Global Object)
2. 变量声明的变量名作为GO的属性名,值赋予undefined
3.找函数声明,值赋予函数体
-
// 小栗子
-
console.
log(test);
-
function
test(
test) {
-
console.
log(test);
-
var test =
123;
-
console.
log(test);
-
function
test(
) {}
-
}
-
test(
1)
-
-
// 输出 function test(test){} fucntion test() {} 123
-
// 小栗子
-
var
global =
100;
-
function
fn(
) {
-
console.
log(
global);
-
}
-
fn();
-
-
首先
GO {
-
global:
undefined --->
100,
-
fn: fucntion
fn(
) {...}
-
}
-
在代码执行到
fn() 前一刻
-
AO{
-
啥也没有
-
}
-
-
在
console.
log(
global)时,由于
AO里面啥也没有,它会向
GO里面找 此时的
global为
100 输出
100
-
// 稍微有点难的哦
-
global =
100;
-
function
fn(
) {
-
console.
log(
global); 此时找到fn函数内有变量声明
global
undefined
-
global =
200;
-
console.
log(
global);
-
var
global =
300;
-
}
-
fn();
-
var
global;
-
-
输出
undefined
200 因为
AO中有
global
[[scope]] (对象隐式的属性)
每个JavaScript函数都是一个对象,对象的某些属性我们可以访问,有些不可以,这些属性仅供JavaScript引擎存取,[[scope]]就是其中的一个,[[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合 (执行期上下文,即预编译,函数预编译创建AO{} --> 函数执行完之后销毁AO{} ---> 当再次执行前函数预编译 ---> 创建AO{} ---> 用完就销毁了)
作用域链
[[scope]] 中所存储的执行期上下文的集合,这种集合呈链式连接,我们把这种叫做作用域链
查找变量
作用域链变量的查找是从作用域的顶端依次向下查找,在哪个函数中找一个变量就去哪个函数的作用域链里查找
-
// 函数小栗子分析作用域链
-
function
a(
) {
-
function
b(
) {
-
var bb =
234;
-
a =
0;
-
}
-
var aa =
123;
-
b();
-
console.
log(aa);
-
}
-
var glob =
100;
-
a();
-
当代码被执行的时候,创建GO对象,并将GO放到[[scope]]作用域顶端, 执行全局预编译,将glob变量和函数a提升到GO中,当执行到a()的前一刻,a函数执行函数预编译,创建AO对象,AO放到[[scope]]顶端,GO在AO下面,此时aa变量和b函数提升,放到AO对象中,在执行b()函数的前一刻,b函数执行函数预编译,创建一个新的AO对象,bb变量提升,a变量放到最开始的GO对象中,此时的AO放到最前面,下面依次是a函数的AO和全局的GO对象,如下图
a函数执行的时候:
b函数执行的时候:
总结:
当b函数执行完的时候,b函数回到定义的状态,销毁自己的AO(只能销毁自己的AO),剪短第三张图中0与AO的线,当b函数再次被执行的时候,会重新产生一个AO放到[[scope]]顶部,当b执行完,a函数内部代码被执行完,a函数剪掉第二张图0与AO的线,a会回到定义的状态。
如果a不执行,b就不会被定义。
当内容函数被保存到外部时,将会产生闭包,导致原油作用域链不释放,造成内存泄露。
闭包的作用 实现公有变量
-
// 实现一个累加器 使用闭包
-
function
add(
) {
-
var count =
0;
-
function
demo(
) {
-
count++;
-
console.
log(count);
-
}
-
return
demo();
-
}
-
-
var counter =
add();
-
counter();
// 输出1
-
counter();
// 输出2
可以做缓存
-
// 使用闭包做一个缓存
-
function
test(
) {
-
var num =
100;
-
function
a(
) {
-
num++;
-
console.
log(num);
-
}
-
function
b(
) {
-
num--;
-
console.
log(num);
-
}
-
return [a, b];
-
}
-
var myArr =
test();
-
myArr[
0]();
// 输出101
-
myArr[
1]();
// 输出100
备注:在一个函数中,有多个子函数的话,这几个子函数公共指向同一个复函数产生的作用域链,即父函数的
test define test [[scope]] 0 GO
test doing test [[scope]] 0 AO 1 GO
定义:凡是写的函数只想被执行一次,执行完一次后被销毁的函数叫做初始化功能函数,即立即执行函数
-
// 立即执行函数不传参格式
-
(
function(
) {
-
var a =
123;
-
var b =
234;
-
console.
log(a, b);
-
}())
-
// 输出123 234
-
-
-
// 传参格式
-
(
function(
a, b, c) {
-
console.
log(a, b, c);
-
}(
1,
2,
3))
-
// 輸出1 2 3
两种格式:
(function() {}()) ()在里面 ---> 建议这种(w3c标准)
(function() {})() ()在外面
注意:只有表达式才能被执行
-
// 小栗子
-
fucntion
test(
) {} () ---> 不会被执行,因为test是函数声明不是函数表达式
-
123;
234;
123,
234 ----> 可以叫做表达式
-
var test =
function(
) {
console.
log(
'a')} () ----> 能执行 输出a
被执行函数执行的表达式就成了立即执行函数,并忽略函数名引用
-
// 小栗子
-
var test =
function(
) {
console.
log(
'a') };
-
执行
test() test --> 输出为
function
-
-
当写成
var test =
fucntion(
) {
console.
log(
'a')} ()
-
执行完之后 test --->
undefined
立即执行函数的几个题
-
// 给ui下的四个li绑定一个事件,输出每个li的位置
-
function
test(
) {
-
var liCollection =
document.
getElementsByTagName(
'li');
-
var len = liCollection.
length;
-
for(
var i =
0; i < len; i ++) {
-
(
function(
j) {
-
liCollection[j].
onclick =
function(
) {
-
console.
log(j +
1)
-
}
-
}(i))
-
}
-
}
-
// 如果没有立即执行函数,每个li输出的i都是li标签的数量,因为当li被点击的时候,li函数的GO作用域中的i已经变成了len
-
-
-
-
// 求一个字符串的字节长度(提示:字节串有一个方法,charCodeAt();一个中文占两个字节,一个英文占一个字节) charCodeAt返回指定位置的字符串的Unicode编码,这个返回值是0~65535之间的整数。(当返回值<= 255时为英文,>时就是中文)
-
-
function
retByteSlean(
target) {
-
if(
typeof(target) !==
"string") {
-
return;
-
}
-
var len = target.
length,
-
count = len;
-
for(
var i =
0; i < len; i++) {
-
if(target[i].
charCodeAt() >
255) {
-
count++;
-
}
-
}
-
return count;
-
}
原理:
逗号表达式会先计算表达式前面的值,再计算后面的值,最后打印后面的值。
-
// 小栗子
-
var a = (
2 +
1,
2 -
1);
-
输出
1
-
-
// 对对象的增删改查
-
var obj = {
name:
'zl',
age:
18};
-
-
增 对象 + . + 属性名 + : + 属性值 --> obj.
lastName =
'lei'
-
删
delete + 对象.属性名 --->
delete obj.
name
-
改 obj.原属性名 = 需要改的属性值 ---> obj.
age =
17
-
查 obj.属性名, 原对象没有该属性名,返回
undefined ---> obj.
name 输出 zl obj.
height 输出
undefined
-
-
// 第一种 对象字面量/对象直接量
-
var obj = {...}
-
-
// 第二种 (通过构造函数构造对象)
-
1. 系统自带
Object构造函数,通过
Object来创建对象,
Object系统相当于一个工厂,它可以批量地生产对象,生产出来的对象一毛一样,但是每个对象都相互独立,系统自带其他构造函数,如
Array、
Number等
-
当
new
Object(),系统会给你一个真正的对象,js中的对象相对于
Java和C++的对象更加灵活,js中的对象更像人的出生,系统自带
Object的使用
-
var obj =
new
Object();
-
obj.
name =
'zl';
-
-
2.自定义的构造函数
-
function
Person(
) {}
-
因为构造函数跟普通函数看起来没什么区别,所以构造函数在命名时要遵循大驼峰规则(单反是单词首字母都需要大写)这样来区别
-
|
-
—— 小栗子
-
function
Car(
color) {
-
this.
height =
1400;
-
this.
weight =
1000;
-
this.
lang =
4900;
-
this.
color = color;
-
}
-
var car1 =
new
Car(
'red');
-
var car2 =
new
Car(
'green');
-
|
-
_ 解析
-
Car 相当于一个车间,给同一个车型车刻了一个模子,color是可选的同款车型的不同颜色,height、weight、lang是该款车型不变的配置,
Car生产出来的车相互独立,互不影响(互不影响就可以用来做原型链的继承)
-
1. 在函数体最前面会有一个隐式的
this对象
-
function
Student(
) {
-
// var this = {};
-
}
-
var
this = {} 相当于在
AO中加入
this属性名和{}属性值
AO{
this: {}}
-
刚开始的时候这个
this指向
window,当
new 该函数的时候,
this指向__proto__
-
-
2.执行
this.
xxx = xxx
-
-
3.隐式返回
this ==> 这一步是
new之后返回的
-
function
Student(
name, age) {
-
this.
name = name;
-
this.
age = age;
-
// new之后 代码执行到这里相当于 AO{this: {name: name, age: age}}
-
// 最后隐式返回this 如果你强制return {} 会替换return的this, 如果你return的是原始值,
-
// 不会影响this对象的返回
-
}
-
-
// 如果不执行new,就是普通的 Student(1,2) 这里的this指向window,这里的name和age是给window上赋值,执行window.name window.age 分贝输出1 和 2
在JavaScript中数字有两种个数字,字符串分两种字符串,布尔值分为两种布尔值,原始值数字才是原始值,原始值字符串才是原始值,这里还有非原始值数字和非原始值字符串,new Number(xxx) 默认是Number {0} 、new String(xxx) String {""}、 new Boolean() 默认是Boolean {false}
注意:undefined和null没有包装类
-
// 包装类有自己的属性和方法
-
var num =
new
Number(
123);
-
num.
abc =
"ac";
-
console.
log(num.
abc);
// 输出 “ac”
-
num.
fn =
function(
) {
console.
log(
123) };
-
num.
fn()
// 输出 123
-
-
// 但是原始值本身没有属性和方法,也不能加属性和方法,也不能访问属性和方法
-
var num =
3;
-
num.
length =
4;
// 原始值num是没有length属性的,它会转换为new Number(3).length = 4,执行完这段代码之后Number销毁
-
console.
log(num.
length);
// 此时又重新转换为new Number(3),此时输出length为undefined。
-
-
// 面试题
-
var str =
"abc";
-
str +=
1;
-
var test =
typeof(str);
-
if(test.
length ===
6) {
-
test.
sign =
"typeof返回的值可能是string";
// 转换为包装类之后销毁
-
}
-
console.
log(test.
sign)
// 再次转换为包装类 因为new String("test")没有sign这个属性,输出为undefined 原始值没有方法和属性
-
// 输出为undefined
定义:原型是function对象的一个属性,它定了构造函数制造出来的对象的公共祖先,通过构造函数生产的对象可以继承该原型的属性和方法,原型也是对象。
我们创建出来的对象,都会继承了父级的prototype,他不仅有自己定义的属性,还有“他爹”(原型上)的属性,当我们构造出来的对象有公共的不改变的属性,我们可以把这些不变的属性到原型上面
-
// 小栗子
-
function
Car(
color, ower) {
-
this.
color = color;
-
this.
ower = ower;
-
this.
height =
1400;
-
this.
weight =
4900;
-
}
-
var car =
new
Car(
"red",
"lei.zhou");
-
-
// 每次new Car()都会走一遍 this.height this.weight, 而这两个属性是不变的,可以直接放到Car的原型上面
-
Car.
prototype = {
height:
1400,
weight:
4900}
-
// 小栗子
-
Person.
prototype.
lastName =
"lei";
-
function
Person (name) {
-
this.
name = name;
-
}
-
var person =
new
Person(
"张");
-
-
// 原型的增
-
Person.
prototype.
firstName=
"zhou";
-
// 原型的删
-
delete
Person.
prototype.
firstName; 返回
true
-
// 原型的改
-
Person.
prototype.
lastName =
"leizi";
-
// 原型的查
-
Person.
prototype.
firstName; 删除
"zhou"
-
// 原型的两种写法
-
第一种
XX.
prototype.
xx = xx
-
Person.
prototype.
name =
'buhaoba';
-
第二种
XX.
prototype = {
xx: xx}
-
Person.
prototype = {
name:
"buhaoba"};
-
// 查看对象的构造函数
-
function
Car(
) {}
-
var car =
new
Car();
-
console.
log(car.
constructor);
// 输出为 function Car(){}
-
-
// car.constructor有时候也会找错生产自己的工厂
-
function
Person(
) {}
-
car.
prototype = {
-
constructor:
Person
-
}
-
function
Car(
) {}
-
var car =
new
Car();
-
console.
log(car.
constructor);
// 输出为Person
-
Person.
prototype.
lastName =
"abc";
-
function
Person(
) {}
-
var person =
new
Person();
-
console.
log(person);
从上图可以知道输出person.name时会沿着__proto__找到Person.prototype,__proto__跟Person.prototype指向同一个地址,他两都是引用值,在原型中找对应的属性,Person.prototype属性有lastName和constructor,constructor这个指向function Person() {},Person.prototype也有自己的__proto__,这个__proto__指向Object.prototype
-
Person.
prototype.
name =
"sunny";
-
function
Person(
) {}
-
var person1 =
new
Person();
-
Person.
prototype = {
name:
'abc'};
-
console.
log(person1.
name);
// 输出为"sunny"
-
var person2 =
new
Person();
-
console.
log(person2.
name);
// 输出为"abc"
-
-
-
Person.
prototype.
name =
"sunny";
-
function
Person(
) {}
-
var person =
new
Person();
-
Person.
prototype.
name =
'abc';
-
console.
log(person.
name);
// 输出为"abc"
为啥会出现以上这种情况呢,原因竟然是:
第一种:打印person1.name 沿着__proto__找Person.prototype,此时的Person.prototype的指向为stack0001,这里面的name为“sunny”,所以输出“sunny”;打印person2.name 沿着__proto__找Person.prototype,Person.prototype重新赋值一个对象,指向发生了改变,此时的Person.prototype的指向为stack0002,这里面的name为“abc”,所以输出“abc”。
第二种:打印person.name 沿着__proto__找Person.prototype,此时的Person.prototype的指向为stack0001,这里面的name从“sunny”变成了“abc”,指向还是之前的 stack0001,只是内容改变了,所以输出“abc”。
-
Grand.
prototype.
lastName =
"lei";
-
function
Grand(
) {
-
this.
age =
1000;
-
}
-
var grand =
new
Grand();
-
Father.
prototype = grand;
-
function
Father(
) {
-
this.
height =
123;
-
}
-
var father =
new
Father();
-
Son.
prototype = father;
-
function
Son(
) {
-
this.
weight =
"henqin";
-
}
-
var son =
new
Son();
-
console.
log(son);
-
// 原型链的增
-
构造函数名.
prototype.
xx = xx;
-
Father.
prototype.
happyState =
true;
-
console.
log(son.
happyState); 输出为
true
-
// 原型链的删
-
delete 构造函数名.
prototype.
xx
-
delete
Father.
prototype.
happyState;
-
console.
log(son.
happyState); 输出为
undefined
-
// 原型链的改 (改值的时候不要直接等于一个引用值,这样会让原型链断裂)
-
构造函数名.
prototype.原有属性名= xx;
-
Father.
prototype.
age =
15;
-
console.
log(son.
age ); 输出为
15
-
// 原型链的查
-
从儿子级一直到祖先查,如果祖先有和自己一样的属性,输出自己的
-
// 小栗子
-
function
Person(
) {}
-
var person =
new
Person;
-
person.
toString(); 输出 “[object
Object]”
为啥会输出呢,是因为Person的__proto__指向Object.prototyepe,Object.prototyepe里有toString这个属性
-
var obj =
Object.
create(
"原型 可以自己指定");
-
var prop = {
-
name:
123
-
}
-
var obj1 =
Object.
create(prop);
-
console.
log(obj1);
如果create里面传值为null时,他就不继承自Object.prototype
-
var obj =
Object.
create(
"原型 可以自己指定");
-
var prop =
null;
-
var obj1 =
Object.
create(prop);
-
console.
log(obj1);
-
var num =
123;
-
num.
toString(); 输出为“
123”
-
为啥会输出字符串
123呢,因为
Number.
prototype原型上面有 toString =
function(
) {}
-
Number.
prototype中没有的属性才会找
Object.
prototype上的属性
-
-
// 包装类(Number String Boolean) 和 Array Object都有自己的toString方法
-
调用的方法不同,输出的结果也不同
-
-
Object.
prototype.
toString.
call(
123) 输出
"[object Number]"
-
function
Person(
name, age) {
-
this.
name = name;
-
this.
age = age;
-
}
-
var person =
new
Person(
"zhoulei",
18);
-
var obj = {};
-
Person(
1,
2); 相当于
Person.
call(
null,
1,
2);
call 和 apply进行改变this指向的芳芳详解
-
// call 改变this指向 第一个参数是this,this需要指向的函数 之后的参数是需要进行赋值的参数,用‘,’分隔传递
-
function
a(
name, age) {
-
console.
log(
123);
-
this.
name = name;
-
this.
age = age;
-
}
-
function
b(
name, age, sex) {
-
a.
call(
this, name, age);
-
this.
sex = sex;
-
}
-
var item =
b(
1,
2,
3);
-
-
-
// apply改变this指向 第一个参数是this,this需要指向的函数 之后的参数是需要进行赋值的参数,需要传递一个数组
-
function
a(
name, age) {
-
console.
log(
123);
-
this.
name = name;
-
this.
age = age;
-
}
-
function
b(
name, age, sex) {
-
a.
call(
this, name, age);
-
this.
sex = sex;
-
}
-
var item =