声明:文章的绝大部分内容来自于《javaScript学习指南》和《javaScript忍者秘籍》
在javascript中对象也是中容器,区别数组的是,里面的对象属性是无序的。
个人感觉类似python的字典。
for … in 是枚举对象属性的传统方式:
const SYM = Symbol();
const o = {a:1,b:2,c:3,[SYM]:4};
for(let prop in o){
document.write(`${prop}:${o[prop]}<br />`);
`输出:
a:1
b:2
c:3
`
}
这里有个注意点:for … in不会枚举键为符号的属性。
还有遍历数组推荐使用for…of或者forEach
<script>
const v = [1,2,3,4];
for (let each of v){
document.write(each+"<br \>");
};
`输出如下:
1
2
3
4
`
</script>
Object.keys对象的所有属性,返回的是一个数组。
<script>
`
箭头函数:
x=> x*x*
相当于:
function(x){
return x*x;
}
`
const SYM = Symbol();
const o = {a:1,b:2,c:3,[SYM]:4};
Object.keys(o).forEach(prop =>
document.write(`${prop}:${o[prop]}<br />`));
`输出:
a:1
b:2
c:3
`
</script>
其他的几种遍历方式,查看链接:
https://www.cnblogs.com/neverleave/p/6134446.html
到了重点的内容:面向对象编程
编程语言的面向对象的思想都是一致的,但是可能实现的方式不一样。
ES6之前没有直接创建类的方法,class关键字是在之后ES6中出现的。
参考:
http://www.jb51.net/article/84089.htm
http://blog.csdn.net/pcaxb/article/details/53759637
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes
<script>
class Car{
};
let car1 = new Car();
let bird = {};
alert(typeof car1);//object
alert(typeof bird);//object
</script>
判断一个实例是否是某个类的: car1 instanceof Car
示例:
<script>
class Person{
constructor(name,birth){
this.name=name;
this.birth=birth;
this.shenxiao=["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"];
}
myshenxiao(){
let s = (this.birth-1984)%12;
if (s<0)s +=12;
alert(`${this.name}:你的生肖是:${this.shenxiao[s]}`);
}
showyourself(word){
alert(word)
}
}
p1 = new Person("zy",1970);
p1.myshenxiao();//zy:你的生肖是:狗
p1.showyourself("hi girls");//hi girls
</script>
这里有个方法比较特殊的,就是类的构造方法:constructor
this关键字引用了方法被调用时所绑定的实例。
动态属性
大部分面向对象的语言提供了一种机制防止类方法被改写。而javascript没有这种机制,这是缺陷。如上面的代码修改下:
p1 = new Person("zy",1970);
p1.shenxiao=[1,2,3,4,5,6,7,8,9,10,11,12];
p1.myshenxiao();//zy:你的生肖是:11
p1.showyourself("hi girls");//hi girls
他将类属性shenxiao改写了,导致错误的结果。
动态属性可以稍微弥补这种不足。
<script>
class Person{
constructor(name,birth){
this.name=name;
this.birth=birth;
this._shenxiao=["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"];
}
get shenxiao(){ return this._shenxiao}
myshenxiao(){
let s = (this.birth-1984)%12;
if (s<0)s +=12;
alert(`${this.name}:你的生肖是:${this.shenxiao[s]},${this.birth}`);
}
}
p1 = new Person("zy",1994);
p1.shenxiao = [1,2,3,4,5,6,7,8,9,10,11,120];
alert(p1.shenxiao);//鼠,牛,虎,兔,龙,蛇,马,羊,猴,鸡,狗,猪
p1.myshenxiao();//zy:你的生肖是:狗,1994
</script>
get和set方法:对某个属性设置存值函数和取值函数, 拦截该属性的存取行为。
但是这种行为就很难说,如下,还是会修改
p1 = new Person("zy",1994);
alert(p1.shenxiao);//鼠,牛,虎,兔,龙,蛇,马,羊,猴,鸡,狗,猪
alert(p1._shenxiao);//鼠,牛,虎,兔,龙,蛇,马,羊,猴,鸡,狗,猪。我就直接去修改p1._shenxiao
p1._shenxiao = [1,2,3,4,5,6,7,8,9,10,11,12];
alert(p1.shenxiao);//[1,2,3,4,5,6,7,8,9,10,11,12];
alert(p1._shenxiao);//[1,2,3,4,5,6,7,8,9,10,11,12];
p1.myshenxiao();//zy:你的生肖是:11,1994
http://www.jb51.net/article/127076.htm
为了类的属性真正私有,先停下说下函数。
《javascript学习指南》中说到(因为我也没去官网看过):class的底层实现并没有发生变化(和之前的es5比),class只是增加了一些语法糖。类实际上还是函数。
<script>
class Person{
}
alert(typeof Person);//输出:function.
</script>
所以更深层的了解函数:
1.无参普通函数
function hello(){
return 1;
};
t = hello();
alert(t);//1
需要一提的是引用调用:函数名作为是可以赋值给变量的
let f = hello;
f();//等同hello();
//或者
let o = {};
o.f = hello;
o.f();
//或者
let a =[];
a.push(hello);
a[0]();
//或者
const o = {
name:'zy',
bark:function(){return 1};
}
//改写如下:es6新特性
const o = {
name :'zy',
bark(){return 1}
}
o.bark();
2.有参普通函数:
//javascript中没有函数重载,所以,只要是相同的函数名,那么不管0,1,2,n参数都是不区分的,重新定义的话就会覆盖。
<script>
function hello(a){
alert(2);
}
function hello(){
alert(1);
};
hello(2);//结果还是1
</script>
//所以带参的函数:
function addAB(a,b){
return a+b;
};
//不确定参数:使用...来收集
<script>
function addAll(...num){
let t = 0;
for ( x of num){
t = t+x;
}
alert(t);
}
addAll(1,2,3,4,5,6);//21
</script>
3.匿名函数和函数表达式
const g =function() {
...
}
g();
关于函数和函数表达式的区别:Function Expression 将函数定义为表达式语句(通常是变量赋值)的一部分。通过 Function Expression 定义的函数可以是命名的,也可以是匿名的。
可以注意点:const g = function (){} 和 const g = function f (){}区别:
如果后一种直接调用 f(),直接报错:f is not defined
但是后一种可以自身调用自身:
const g = function f(){
//代码。。。
f()
};
g()
4.箭头函数举例:
const f1 = function(){return 1}
const f1 =() => 1;
const f2 = function(name) {return name}
const f2 = name => name;
const f3 = function(a,b){return a+b}
const f3 = (a,b) => a+b;
调用、请求和绑定
http://blog.csdn.net/baidu_36831253/article/details/79310279
函数的作用域:全局作用域、块作用域函数作用域
块:是由一对花括号括起来的一系列语句。那么,块作用域指的就是那些仅仅在代码块内有效的变量。let和const关键字声明的变量名处在块作用域中。
{
let t = 1;
};
document.write(t);//报错。但是如果使用 var t =1;那么就正常。这就是var的隐患。
重点来了,闭包函数
有一个十分常见的场景:故意将某个函数定义在一个指定的作用域中,并明确地指出它对该作用域所具有的访问权限,通常称这种形式为闭包。
闭包概念:
闭包就是有权访问另一个函数作用域中变量的函数.
闭包可以让函数访问所有的变量和函数,只要这些变量和函数存在于该函数声明时的作用域内就行。
示例:
<script>
var outvalue =1;
var later;
function outFunc(){
var innervalue = 2;
function innerFunc(){
document.write(outvalue+"<br>");//js当然能访问全局作用域中的变量
document.write(innervalue);//重点来了,他能访问innerFunc函数之外,但在outFunc之内的变量
}
later = innerFunc;
}
outFunc();
later();
`
输出:
1
2
`
</script>
重点是:是什么让innervalue在执行内部函数的时候仍然活着,而且还是在作用域小时之后。答案就是闭包。
在外部函数中声明innerFunc()的时候,不仅是声明了函数,还创建了一个闭包,该闭包不仅包含函数声明,还包含了函数声明的那一时刻点上该作用域中所有变量。
最终当innerFunc执行的时候,当时声明的作用域已经消失了,通过闭包,该函数还是能够访问到原始作用域的。
就像一个气泡一样,只要innerFunc函数一直存在,它的闭包就保持该作用域中即将被垃圾回收的变量。
<script>
let outvalue =1 ;
let later;
function outFunc(){
let innervalue = 2;
function innerFunc(paramvalue){
document.write(outvalue+"<br>");
document.write(innervalue+"<br>");
document.write(paramvalue+"<br>");
document.write(toolate+"<br>");
};
later = innerFunc;
}
let toolate = 4;
outFunc();
later(3);
`输出:
1
2
3
4
`
</script>
再来个示例:并不是一定要函数里面含个函数,才叫闭包。块作用域也可以。
let f;//未定义的函数。
{
let o = {note:'notsafe'};
f = function(){
return o;
}
}
let out = f();
alert(out.note);//notsafe
即时调用函数表达式:
语法:
(function(){
//代码;
})();
这里使用函数表达式创建一个匿名函数,然后立即调用该函数。IIFE
const msg = (function(){
const secret = " dandan";
return secret;
})();
alert(msg);//dandan
好处:在IIFEzhong ,变量secret是安全的,并且外部不能访问。
函数提升:
<script>
f();
function f(){
alert(1);//1
}
</script>
允许在声明前调用。因为,js预处理
严格模式:
解决问题:如果忘记了使用var声明某个变量,javascript会不假思索地认为开发人员在引用一个全局变量。如果不存在,则自动创建。这是大问题。
为了解决他。可以使用严格模式,在开始编写代码前,单独插入一行字符串“use strict”.如果在全局作用域这么做,整个脚本都会以严格模式执行。
(function(){a =1})();
alert(a);//1
(function(){"use strict";b=2})();
alert(b);//报错:b is not defind
</script>
继续回到我的面向对象:为了让一个类的属性私有。
实现方式:
https://www.cnblogs.com/ihardcoder/p/4914938.html
1)基于强引用散列表的实现方式
2)基于WeakMap的实现方式,而且WeakMap是一种弱引用散列表
这里又要跳开去说下其他的:
+++++++++++++++++++++++++++++
maps和sets
es6新加的特性:两种数据结构:maps和sets。maps跟对象类似,sets则类似数组,但是里面的不能重复值。在es6之前,当需要把键和值映射起来的时候,一般会首选对象。因为对象能把字符串类型的键映射到任意类型的对象。
<script>
//一、将对象进行映射
const u1 = {name:'Cyntina'};
const u2 = {name:'Jackson'};
const u3 = {name:'olive'};
const user= new Map();
user
.set(u1,'男')
.set(u2,'女')
.set(u3,'人妖');
/*等同于下面的
user.set(u1,'男');
user.set(u2,'女');
user.set(u3,'人妖');
*/
alert(user.get(u2));//女
//通过给map的构造函数传一个数组的数组,将包含了数组的数组进行映射。
const people= new Map([
[u1,'男'],
[u2,'女'],
[u3,'人妖']
]);
alert(people.get(u3));//人妖
//如果key已经在map中了,那么调用set()后key对应的value就会被替换。
people.set(u3,'无性人');
alert(people.get(u3));//无性人
//使用has()查看是否有该key
people.has(u1);//true;
//使用size属性返回map中的元素个数
alert(people.size);//3;
//Keys()方法可以拿到map中所有的键,values()可以拿到所有的值,
//entries()可以拿到以数组的方式返回的键值对。
for(let [u,role] of people.entries())
{
document.write(`${u.name}:${role}`);
//Cyntina:男Jackson:女olive:无性人
}
//因为entries方法是Map的默认迭代器,所以可以这样写:
for(let [u,role] of people)
{
document.write(`${u.name}:${role}`);
//Cyntina:男Jackson:女olive:无性人
}
//delete()方法可以删除map中一个条目
people.delete(u2)
//删除所有条目:
people.clear();
</script>
weak maps
weakmaps跟map在本质上相同的,除了以下几点:
key必须是对象
weakmap中的key可以被垃圾回收
weakmap不能迭代或者清空。
下面这句话解释为啥要用它来进行类的属性私有化:
通常,只要还有地方在引用某个对象,js就会将它保留在内存中。如:如果有一个对象是map中的key,
那么只要这个map存在,这个对象就会一直在内存中。但weakmap却不是这样。正因为如此,
weakmap不能被迭代,因为在迭代过程中,暴露处于垃圾回收过程中的对象,是十分危险的。
将shenxiao的那个程序再次改写如下:
<script>
const Person=(function(){
const privatePerson = new WeakMap();
return class{
constructor(name,birth){
this.name=name;
this.birth=birth;
this._shenxiao=["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"];
privatePerson.set(this,{shenxiao:this._shenxiao});
}
get shenxiao(){return privatePerson.get(this).shenxiao;}
myshenxiao(){
let s = (this.birth-1984)%12;
if (s<0)s +=12;
alert(`${this.name}:你的生肖是:${this.shenxiao[s]}`);
}
showyourself(word){
alert(word)
}
}
})();
p1 = new Person("zy",1970);
alert(p1.shenxiao);//鼠,牛,虎,兔,龙,蛇,马,羊,猴,鸡,狗,猪
p1.shenxiao=[1,2,3,4,5,6,7,8,9,10,11,12];
p1._shenxiao=[1,2,3,4,5,6,7,8,9,10,11,12];
alert(p1.shenxiao);//鼠,牛,虎,兔,龙,蛇,马,羊,猴,鸡,狗,猪
p1.myshenxiao();//zy:你的生肖是:狗
p1.showyourself("hi girls");//hi girls
</script>
对比之前的程序,我们可以发现,之前的如果修改p1._shenxiao还是可以修改p1.shenxiao的。
但是现在两个都试着该,发现,p1.shenxiao改不动。
weak maps的其他示例:
const Girl =(function(){
const girl = new WeakMap();
class meinv{
setType(yourStyle){
girl.set(this,yourStyle)
}
getGirl(){
return `你培养的girl是:${girl.get(this)}`;
}
}
return meinv;
})();
const I = new Girl();
I.setType("萝莉");
alert(I.getGirl());//你培养的girl是:萝莉
I.setType("熟女");
alert(I.getGirl());//你培养的girl是:熟女
const you = new Girl();
alert(you.getGirl());//你培养的girl是:undefined
you.setType("淑女");
alert(you.getGirl());//你培养的girl是:淑女
</script>
上面的重点就是:
weak maps中的key可以被垃圾回收,所以出现了undefind,但是普通的Map就不行
那样会导致yourtype一旦设置就难以回收。