js基础
1.原始类型有哪几种?null 是对象嘛?
string,number,bollean,null,nudefined,symbol
原始类型存放的是值,没有函数可以调用。
null不是对象但是typeof null会输出object这是js悠久的bug
2.对象类型:
原始类型和对象类型不同:原始类型存放的是值,对象类型存储的指针
当创建一个对象类型的时候,计算机会在内存中开辟一个空间存放值,但是我们找到这个空间,这个空间需要一个地址(指针);
const a = []
const b = a
b.push(1),
当我们把一个变量赋值给另外一个变量的时候,复制的是原始变量的指针,当我们进行数据修改的时候,就会修改存放在地址(指针) #001上的值,也就导致了两个变量的值都发生了改变。
function test(person) {
person.age = 26
person = {
name: 'yyy',
age: 30
}
return person
}
const p1 = {
name: 'wgl',
age: 25
}
const p2 = test(p1)
console.log(p1) // -> ?
console.log(p2) // -> ?
1.首先,函数传参是传递对象指针的副本
2.到函数内部修改参数的属性这步, 当前p1 的值也被修改了
3.当到了person重新分配的时候指针发生了变化
3.判断数据类型的几种方法:
1.typeof
返回的数据类型包括:number、boolean、symbol、string、object、undefined、function。
typeof null 返回数据类型错误 返回object
引用类型除了function返回function,其余都是返回object
2.tostring
tostring是Object原型上面的方法,调用该方法,默认返回当前对象的[[Class]],这是一个内部属性,其格式为[Object xxx]对于object对象可以直接调用tostring方法,但是对于其他的就需要借助 call/apply
Object.prototype.toString.call('') ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); //[object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window 是全局对象 global 的引用
3.instanceof
instanceof 是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。 在这里需要特别注意的是:instanceof 检测的是原型,
instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型。
console.log(1 instanceof Number); // false
console.log(true instanceof Boolean); // false
console.log('str' instanceof String); // false
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof Object); // true
4.原型与原型链
Prototype 原型
function Foo(){}
首先介绍 Prototype属性 ,这是一个显示原型属性,只有函数拥有该属性,当函数被创建的时候就产生了这个属性,并且这个属性的值是一个对象,只有一个属性 constructor {constructor:f},constructor对应着构造函数(Foo),它是一个公有且不可枚举的属性,一旦我们改变了函数的prototype,那么新对象就没有constructor属性了(但是可以通过原型链取到constructor)作用:1.让实例对象知道是什么函数构造了它 2.如果想给某些类库中的构造函数增加一些自定义的方法就可以通过xxx.constructor.methed来扩展
proto
每个对象都有隐式原型属性,指向创建该对象的的构造函数的原型。因为js中没有类的概念,为了实现类似继承的方,通过__proto__将对象和原型联系起来组成原型链,得以让对象访问不属于自己的属性。
如何产生:当我们使用new操作符的时候,生成的实例对象拥有了__proto__属性。可以理解为在new的过程中,新对象被添加了__proto__属性链接到了构造函数的原型上。
new的过程
1.新生成了一个对象
2.链接到原型
3.绑定this并执行构造函数
4.返回新的对象
function new(){
//创建一个空的对象
let obj = new Object();
//获得构造函数
let Con = [].shift.call()arguments;
//链接到原型
obj.__proto__ = Con.prototype;
//绑定this执行构造函数
let result = Con.apply(obj,arguments);
//确保new出来是个对象
return typeof result == 'object'?result:obj;
}
总结
1.Object 是所有对象的爸爸,所有对象都可以通过 proto 找到它
2.Function 是所有函数的爸爸,所有函数都可以通过 proto 找到它
3.Function.prototype 和 Object.prototype 是两个特殊的对象,他们由引擎来创建 除了以上两个特殊对象,其他对象都是通过构造器 new 出来的
4.函数的 prototype 是一个对象,也就是原型
5.对象的 proto 指向原型, proto 将对象和原型连接起来组成了原型链
__let const var__区别
- var声明的变量会挂载到window 而var 不会
- var 声明的变量会提升 let const 不会
- let const 会形成块级作用域
- 统一作用域下 let const 不能声明同名变量 而var可以
js进阶
for(var i=0;i<5;i++){
setTimeout(function({
console.log(i)
},1000)
}
输出结果为:5,5,5,5,5
解析:for 循环会先执行完(同步优先于异步优先于回调)setTimeout是异步执行的,1000毫秒后向任务队列里添加一个任务,只有主线上的全部执行完才会执行任务队列里的任务,所以当主线程for循环执行完之后 i的值为5,这个时候再去任务队列中执行任务,i全部为5;
每次for循环的时候setTimeout都会执行,但是里面的function则不会执行被放入任务队列,因此放了5次;for循环的5次执行完之后不到1000毫秒;
因为 setTimeout 的 console.log(i); 的i是 var 定义的,所以是函数级的作用域,不属于 for 循环体,属于 global。等到 for 循环结束,i 已经等于 5 了,这个时候再执行 setTimeout 的五个回调函数(参考上面对事件机制的阐述),里面的 console.log(i); 的 i 去向上找作用域,只能找到 global下 的 i,即 5。所以输出都是 5。
如何修改后输出01234
1.方式一 使用自执行函数
for (var i = 0; i < 5; i++) {
(function(i){ //立刻执行函数
setTimeout(function (){
console.log(i);
},1000);
})(i);
}
解析:这里用到立刻执行函数。这样 console.log(i); 中的i就保存在每一次循环生成的立刻执行函数中的作用域里了。
2.方式二
for(let i=0;i<5;i++){
setTimeout(function({
console.log(i)
},1000)
}
解析:let 为代码块的作用域,所以每一次 for 循环,console.log(i); 都引用到 for 代码块作用域下的i,因为这样被引用,所以 for 循环结束后,这些作用域在 setTimeout 未执行前都不会被释放。
3.方式三
var output = function(i){
setTimeout(function(){
console.log(i)
},1000)//闭包
},
for(var i=0;i<5;i++){
output(i);
}
如何输出012345
1.方式一
for(var i=0;i<5;i++){
(function(j){
setTimeout(function(){
console.log(j)
},1000*j)
})(i)
}
setTimeout(function(){
console.log(i)
},1000*i)
2.方式二:
var task = [];
for(var i=0;i<5;i++){
(function(j){
setTimeout(function(){
task.push(new Promise(resolve=>{
console.log(j)
resolve()
}))
},1000*j)
})(i)
}
Pormise.all(task).then(data=>{
console.log(i)
})
笔试
var n = 123;
function fn1(){
console.log(n);//所以这里面的n指的的全局作用域的n
}
function fn2(){
var n = 456
fn1(n)//无调用者 等价于 window.fn1()
}
fn2()//无调用者 等价于 window.fn2()
console.log(n)
最终输出结果为:123,123
var length = 100;
function f1(){
console.log(this.length);
}
var obj = {
x:10,
fn2:function(f1){
f1();//无调用者 this指向window 输出100
arguments[0]();//无调用者 等价于 arguments.f1();this指向arguments这个对象
}
}
obj.fn2(f1,1)
function fn1(){
console.log(this.a)
}
var obj = {
a:1,
f:f
}
var f2 = obj.f;
var a = 'hello';
f2();// obj.f 没有执行,只是赋值,这里f2()无调用者 this指向window最终输出 hello