无法读取 get 属性 of undefined angular 2 错误
can not read get property of undefined angular 2 error
您好,我正在尝试从 google api 获取城市名称,但下面的错误是我的代码
应用组件class
import {Component, OnInit} from 'angular2/core';
import {marketComponent} from './market.component';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {introComponent} from './intro.component';
import {geoService} from './service.geo';
import {JSONP_PROVIDERS} from 'angular2/http';
declare var google: any;
@Component({
selector: 'my-app',
templateUrl: 'app/app.component.html',
directives: [ROUTER_DIRECTIVES],
providers: [JSONP_PROVIDERS, geoService]
})
@RouteConfig([
{ path: '/intro', name: 'Intro', component: introComponent, useAsDefault: true },
{ path: '/market', name: 'Market', component: marketComponent },
])
export class AppComponent {
constructor(private _http: geoService) { }
public maps;
public cat_error: Boolean = false;
public xml_Latitude :string;
public xml_Lang: string;
ngOnInit() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(this.showPosition);
} else {
alert("Geolocation is not supported by this browser.");
}
var input: any = document.getElementById('google_places_ac');
var autocomplete = new google.maps.places.Autocomplete(input, {});
google.maps.event.addListener(autocomplete, 'place_changed', function () {
var place = autocomplete.getPlace();
console.log(place)
});
}
showPosition(position) {
this.xml_Latitude = position.coords.latitude;
this.xml_Lang = position.coords.longitude;
this._http.getPlaces(this.xml_Latitude, this.xml_Lang).subscribe(
data => { this.maps = data },
err => { this.cat_error = true }
);
var result = this.maps.results;
var city = result[0].address_components[4].long_name + "," + result[0].address_components[6].long_name;
alert(city);
}
}
和地理服务文件
import {Injectable} from 'angular2/core';
import { Response, Jsonp} from 'angular2/http';
import 'rxjs/add/operator/map';
@Injectable()
export class geoService {
constructor(private http: Jsonp) { }
public xml_Latitude: string;
public xml_Lang: string;
public getPlaces(xml_Latitude, xml_Lang) {
return this.http.get(`http://maps.googleapis.com/maps/api/geocode/json?latlng=
'${this.xml_Latitude}','${this.xml_Lang}'&sensor=true`)
.map((res: Response) => res.json())
.catch(this.handleError);
}
private handleError(error: Response) {
console.error(error);
return error.json().error || 'Server error';
}
}
错误还说 getplaces 不是函数,我想我遗漏了什么但不知道是什么....
我认为您应该将 result
块移动到与 getPlaces 方法调用关联的订阅回调中:
showPosition(position) {
this.xml_Latitude = position.coords.latitude;
this.xml_Lang = position.coords.longitude;
this._http.getPlaces(this.xml_Latitude, this.xml_Lang).subscribe(
data => {
this.maps = data;
var result = this.maps.results; // <----------
var city = result[0].address_components[4].long_name + "," + result[0].address_components[6].long_name;
alert(city);
},
err => { this.cat_error = true }
);
}
因为this.maps
在回调被调用前未定义。然后您尝试在 (this.maps.results
).
之前获取 result
属性
编辑
我在 navigator.geolocation.getCurrentPosition
行也看到了问题。您可以这样重构您的代码:
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => { // <----
this.showPosition(position);
});
} else {
alert("Geolocation is not supported by this browser.");
}
除了 Thierry 确定的回调排序问题之外,您还丢失了此行的 this
上下文:
navigator.geolocation.getCurrentPosition(this.showPosition);
问题
您有 classic Java脚本问题,称为 不正确的 this
上下文 。
this
keyword in JavaScript 的行为不同于其他语言,如 C# 和 Java。
this
的工作原理
this
关键字,在函数中,确定如下:
* 如果函数是通过调用 .bind
创建的,this
值是提供给 bind
的参数
* 如果函数是通过方法调用 调用的 ,例如expr.func(args)
,那么this
就是expr
* 除此以外
* 如果代码在strict mode,this
就是undefined
* 否则,this
是 window
(在浏览器中)
让我们看看这在实践中是如何工作的:
class Foo {
value = 10;
doSomething() {
// Prints 'undefined', not '10'
console.log(this.value);
}
}
let f = new Foo();
window.setTimeout(f.doSomething, 100);
此代码将打印 undefined
(或者,在严格模式下,抛出异常)。
这是因为我们最终到达了上面决策树的最后一个分支。
调用了 doSomething
函数,该函数不是 bind
调用的结果,也不是在方法语法位置调用的。
我们看不到 setTimeout
的代码来了解它的调用是什么样的,但我们不需要。
需要注意的是,所有 doSomething
方法都指向 相同的函数对象 。
也就是说:
let f1 = new Foo();
let f2 = new Foo();
// 'true'
console.log(f1.doSomething === f2.doSomething);
我们知道setTimeout
只能看到我们传递给它的函数,所以当它调用那个函数时,
它无法知道要提供哪个 this
。
由于我们 引用 方法而没有 调用 它,this
上下文已经丢失。
红旗
一旦您了解 this
个问题,就很容易发现它们:
class Foo {
value = 10;
method1() {
doSomething(this.method2); // DANGER, method reference without invocation
}
method2() {
console.log(this.value);
}
}
解决方案
这里有几个选项,每个选项都有自己的权衡取舍。
最佳选择取决于从不同调用站点调用相关方法的频率。
Class 定义中的箭头函数
不使用正常的方法语法,而是使用 arrow function 来初始化每个实例成员。
class DemonstrateScopingProblems {
private status = "blah";
public run = () => {
// OK
console.log(this.status);
}
}
let d = new DemonstrateScopingProblems();
window.setTimeout(d.run); // OK
- Good/bad:这会为 class 的每个实例的每个方法创建一个额外的闭包。如果这个方法通常只用在常规方法调用中,那就太过分了。但是,如果它在回调位置使用很多,class 实例捕获
this
上下文而不是每个调用站点在调用时创建一个新的闭包会更有效。
- 好:外部调用者不可能忘记处理
this
上下文
- 好:TypeScript 中的类型安全
- 好:如果函数有参数则无需额外工作
- 错误:派生的 classes 无法调用使用
super.
以这种方式编写的基础 class 方法
- 不好:哪些方法是 "pre-bound" 哪些方法不是的确切语义在您的 class 和它的消费者之间创建了一个额外的非类型安全契约。
参考站点的函数表达式
出于解释原因,此处显示了一些虚拟参数:
class DemonstrateScopingProblems {
private status = "blah";
public something() {
console.log(this.status);
}
public run(x: any, y: any) {
// OK
console.log(this.status + ': ' + x + ',' + y);
}
}
let d = new DemonstrateScopingProblems();
// With parameters
someCallback((n, m) => d.run(n, m));
// Without parameters
window.setTimeout(() => d.something(), 100);
- Good/bad:与第一种方法
相反memory/performance权衡
- 好:在 TypeScript 中,这具有 100% 的类型安全性
- 好:适用于 ECMAScript 3
- 很好:您只需输入一次实例名称
- 不好:您必须输入两次参数
- 不好:不能轻易使用可变参数
您好,我正在尝试从 google api 获取城市名称,但下面的错误是我的代码
应用组件class
import {Component, OnInit} from 'angular2/core';
import {marketComponent} from './market.component';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {introComponent} from './intro.component';
import {geoService} from './service.geo';
import {JSONP_PROVIDERS} from 'angular2/http';
declare var google: any;
@Component({
selector: 'my-app',
templateUrl: 'app/app.component.html',
directives: [ROUTER_DIRECTIVES],
providers: [JSONP_PROVIDERS, geoService]
})
@RouteConfig([
{ path: '/intro', name: 'Intro', component: introComponent, useAsDefault: true },
{ path: '/market', name: 'Market', component: marketComponent },
])
export class AppComponent {
constructor(private _http: geoService) { }
public maps;
public cat_error: Boolean = false;
public xml_Latitude :string;
public xml_Lang: string;
ngOnInit() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(this.showPosition);
} else {
alert("Geolocation is not supported by this browser.");
}
var input: any = document.getElementById('google_places_ac');
var autocomplete = new google.maps.places.Autocomplete(input, {});
google.maps.event.addListener(autocomplete, 'place_changed', function () {
var place = autocomplete.getPlace();
console.log(place)
});
}
showPosition(position) {
this.xml_Latitude = position.coords.latitude;
this.xml_Lang = position.coords.longitude;
this._http.getPlaces(this.xml_Latitude, this.xml_Lang).subscribe(
data => { this.maps = data },
err => { this.cat_error = true }
);
var result = this.maps.results;
var city = result[0].address_components[4].long_name + "," + result[0].address_components[6].long_name;
alert(city);
}
}
和地理服务文件
import {Injectable} from 'angular2/core';
import { Response, Jsonp} from 'angular2/http';
import 'rxjs/add/operator/map';
@Injectable()
export class geoService {
constructor(private http: Jsonp) { }
public xml_Latitude: string;
public xml_Lang: string;
public getPlaces(xml_Latitude, xml_Lang) {
return this.http.get(`http://maps.googleapis.com/maps/api/geocode/json?latlng=
'${this.xml_Latitude}','${this.xml_Lang}'&sensor=true`)
.map((res: Response) => res.json())
.catch(this.handleError);
}
private handleError(error: Response) {
console.error(error);
return error.json().error || 'Server error';
}
}
错误还说 getplaces 不是函数,我想我遗漏了什么但不知道是什么....
我认为您应该将 result
块移动到与 getPlaces 方法调用关联的订阅回调中:
showPosition(position) {
this.xml_Latitude = position.coords.latitude;
this.xml_Lang = position.coords.longitude;
this._http.getPlaces(this.xml_Latitude, this.xml_Lang).subscribe(
data => {
this.maps = data;
var result = this.maps.results; // <----------
var city = result[0].address_components[4].long_name + "," + result[0].address_components[6].long_name;
alert(city);
},
err => { this.cat_error = true }
);
}
因为this.maps
在回调被调用前未定义。然后您尝试在 (this.maps.results
).
result
属性
编辑
我在 navigator.geolocation.getCurrentPosition
行也看到了问题。您可以这样重构您的代码:
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => { // <----
this.showPosition(position);
});
} else {
alert("Geolocation is not supported by this browser.");
}
除了 Thierry 确定的回调排序问题之外,您还丢失了此行的 this
上下文:
navigator.geolocation.getCurrentPosition(this.showPosition);
问题
您有 classic Java脚本问题,称为 不正确的 this
上下文 。
this
keyword in JavaScript 的行为不同于其他语言,如 C# 和 Java。
this
的工作原理
this
关键字,在函数中,确定如下:
* 如果函数是通过调用 .bind
创建的,this
值是提供给 bind
的参数
* 如果函数是通过方法调用 调用的 ,例如expr.func(args)
,那么this
就是expr
* 除此以外
* 如果代码在strict mode,this
就是undefined
* 否则,this
是 window
(在浏览器中)
让我们看看这在实践中是如何工作的:
class Foo {
value = 10;
doSomething() {
// Prints 'undefined', not '10'
console.log(this.value);
}
}
let f = new Foo();
window.setTimeout(f.doSomething, 100);
此代码将打印 undefined
(或者,在严格模式下,抛出异常)。
这是因为我们最终到达了上面决策树的最后一个分支。
调用了 doSomething
函数,该函数不是 bind
调用的结果,也不是在方法语法位置调用的。
我们看不到 setTimeout
的代码来了解它的调用是什么样的,但我们不需要。
需要注意的是,所有 doSomething
方法都指向 相同的函数对象 。
也就是说:
let f1 = new Foo();
let f2 = new Foo();
// 'true'
console.log(f1.doSomething === f2.doSomething);
我们知道setTimeout
只能看到我们传递给它的函数,所以当它调用那个函数时,
它无法知道要提供哪个 this
。
由于我们 引用 方法而没有 调用 它,this
上下文已经丢失。
红旗
一旦您了解 this
个问题,就很容易发现它们:
class Foo {
value = 10;
method1() {
doSomething(this.method2); // DANGER, method reference without invocation
}
method2() {
console.log(this.value);
}
}
解决方案
这里有几个选项,每个选项都有自己的权衡取舍。 最佳选择取决于从不同调用站点调用相关方法的频率。
Class 定义中的箭头函数
不使用正常的方法语法,而是使用 arrow function 来初始化每个实例成员。
class DemonstrateScopingProblems {
private status = "blah";
public run = () => {
// OK
console.log(this.status);
}
}
let d = new DemonstrateScopingProblems();
window.setTimeout(d.run); // OK
- Good/bad:这会为 class 的每个实例的每个方法创建一个额外的闭包。如果这个方法通常只用在常规方法调用中,那就太过分了。但是,如果它在回调位置使用很多,class 实例捕获
this
上下文而不是每个调用站点在调用时创建一个新的闭包会更有效。 - 好:外部调用者不可能忘记处理
this
上下文 - 好:TypeScript 中的类型安全
- 好:如果函数有参数则无需额外工作
- 错误:派生的 classes 无法调用使用
super.
以这种方式编写的基础 class 方法
- 不好:哪些方法是 "pre-bound" 哪些方法不是的确切语义在您的 class 和它的消费者之间创建了一个额外的非类型安全契约。
参考站点的函数表达式
出于解释原因,此处显示了一些虚拟参数:
class DemonstrateScopingProblems {
private status = "blah";
public something() {
console.log(this.status);
}
public run(x: any, y: any) {
// OK
console.log(this.status + ': ' + x + ',' + y);
}
}
let d = new DemonstrateScopingProblems();
// With parameters
someCallback((n, m) => d.run(n, m));
// Without parameters
window.setTimeout(() => d.something(), 100);
- Good/bad:与第一种方法 相反memory/performance权衡
- 好:在 TypeScript 中,这具有 100% 的类型安全性
- 好:适用于 ECMAScript 3
- 很好:您只需输入一次实例名称
- 不好:您必须输入两次参数
- 不好:不能轻易使用可变参数